1// Copyright (C) 2017 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 "qqmlprofilerevent_p.h"
5#include <QtCore/qdatastream.h>
6
7QT_BEGIN_NAMESPACE
8
9bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2)
10{
11 if (event1.timestamp() != event2.timestamp() || event1.typeIndex() != event2.typeIndex())
12 return false;
13
14 // This is not particularly efficient, but we also don't need to do this very often.
15 return event1.numbers<QVarLengthArray<qint64>>() == event2.numbers<QVarLengthArray<qint64>>();
16}
17
18bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2)
19{
20 return !(event1 == event2);
21}
22
23enum SerializationType {
24 OneByte = 0,
25 TwoByte = 1,
26 FourByte = 2,
27 EightByte = 3,
28 TypeMask = 0x3
29};
30
31enum SerializationTypeOffset {
32 TimestampOffset = 0,
33 TypeIndexOffset = 2,
34 DataLengthOffset = 4,
35 DataOffset = 6
36};
37
38template<typename Number>
39static inline void readNumbers(QDataStream &stream, Number *data, quint16 length)
40{
41 for (quint16 i = 0; i != length; ++i)
42 stream >> data[i];
43}
44
45template<typename Number>
46static inline Number readNumber(QDataStream &stream, qint8 type)
47{
48 switch (type) {
49 case OneByte: {
50 qint8 value;
51 stream >> value;
52 return static_cast<Number>(value);
53 }
54 case TwoByte: {
55 qint16 value;
56 stream >> value;
57 return static_cast<Number>(value);
58 }
59 case FourByte: {
60 qint32 value;
61 stream >> value;
62 return static_cast<Number>(value);
63 }
64 case EightByte: {
65 qint64 value;
66 stream >> value;
67 return static_cast<Number>(value);
68 }
69 default:
70 Q_UNREACHABLE_RETURN(0);
71 }
72}
73
74QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event)
75{
76 qint8 type;
77 stream >> type;
78
79 event.m_timestamp = readNumber<qint64>(stream, type: (type >> TimestampOffset) & TypeMask);
80 event.m_typeIndex = readNumber<qint32>(stream, type: (type >> TypeIndexOffset) & TypeMask);
81 event.m_dataLength = readNumber<quint16>(stream, type: (type >> DataLengthOffset) & TypeMask);
82
83 uint bytesPerNumber = 1 << ((type >> DataOffset) & TypeMask);
84
85 if (event.m_dataLength * bytesPerNumber > sizeof(event.m_data)) {
86 event.m_dataType = static_cast<QQmlProfilerEvent::Type>((bytesPerNumber * 8)
87 | QQmlProfilerEvent::External);
88 event.m_data.external = malloc(size: event.m_dataLength * bytesPerNumber);
89 } else {
90 event.m_dataType = static_cast<QQmlProfilerEvent::Type>(bytesPerNumber * 8);
91 }
92
93 switch (event.m_dataType) {
94 case QQmlProfilerEvent::Inline8Bit:
95 readNumbers<qint8>(stream, data: event.m_data.internal8bit, length: event.m_dataLength);
96 break;
97 case QQmlProfilerEvent::External8Bit:
98 readNumbers<qint8>(stream, data: static_cast<qint8 *>(event.m_data.external),
99 length: event.m_dataLength);
100 break;
101 case QQmlProfilerEvent::Inline16Bit:
102 readNumbers<qint16>(stream, data: event.m_data.internal16bit, length: event.m_dataLength);
103 break;
104 case QQmlProfilerEvent::External16Bit:
105 readNumbers<qint16>(stream, data: static_cast<qint16 *>(event.m_data.external),
106 length: event.m_dataLength);
107 break;
108 case QQmlProfilerEvent::Inline32Bit:
109 readNumbers<qint32>(stream, data: event.m_data.internal32bit, length: event.m_dataLength);
110 break;
111 case QQmlProfilerEvent::External32Bit:
112 readNumbers<qint32>(stream, data: static_cast<qint32 *>(event.m_data.external),
113 length: event.m_dataLength);
114 break;
115 case QQmlProfilerEvent::Inline64Bit:
116 readNumbers<qint64>(stream, data: event.m_data.internal64bit, length: event.m_dataLength);
117 break;
118 case QQmlProfilerEvent::External64Bit:
119 readNumbers<qint64>(stream, data: static_cast<qint64 *>(event.m_data.external),
120 length: event.m_dataLength);
121 break;
122 default:
123 Q_UNREACHABLE();
124 break;
125 }
126
127 return stream;
128}
129
130static inline qint8 minimumType(const QQmlProfilerEvent &event, quint16 length,
131 quint16 origBitsPerNumber)
132{
133 qint8 type = OneByte;
134 bool ok = true;
135 for (quint16 i = 0; i < length;) {
136 if ((1 << type) == origBitsPerNumber / 8)
137 return type;
138 switch (type) {
139 case OneByte:
140 ok = (event.number<qint8>(i) == event.number<qint64>(i));
141 break;
142 case TwoByte:
143 ok = (event.number<qint16>(i) == event.number<qint64>(i));
144 break;
145 case FourByte:
146 ok = (event.number<qint32>(i) == event.number<qint64>(i));
147 break;
148 default:
149 // EightByte isn't possible, as (1 << type) == origBitsPerNumber / 8 then.
150 Q_UNREACHABLE();
151 break;
152 }
153
154 if (ok)
155 ++i;
156 else
157 ++type;
158 }
159 return type;
160}
161
162template<typename Number>
163static inline qint8 minimumType(Number number)
164{
165 if (static_cast<qint8>(number) == number)
166 return OneByte;
167 if (static_cast<qint16>(number) == number)
168 return TwoByte;
169 if (static_cast<qint32>(number) == number)
170 return FourByte;
171 return EightByte;
172}
173
174template<typename Number>
175static inline void writeNumbers(QDataStream &stream, const QQmlProfilerEvent &event, quint16 length)
176{
177 for (quint16 i = 0; i != length; ++i)
178 stream << event.number<Number>(i);
179}
180
181template<typename Number>
182static inline void writeNumber(QDataStream &stream, Number number, qint8 type)
183{
184 switch (type) {
185 case OneByte:
186 stream << static_cast<qint8>(number);
187 break;
188 case TwoByte:
189 stream << static_cast<qint16>(number);
190 break;
191 case FourByte:
192 stream << static_cast<qint32>(number);
193 break;
194 case EightByte:
195 stream << static_cast<qint64>(number);
196 break;
197 default:
198 Q_UNREACHABLE();
199 break;
200 }
201}
202
203QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event)
204{
205 qint8 type = minimumType(number: event.m_timestamp); // << TimestampOffset;
206 type |= minimumType(number: event.m_typeIndex) << TypeIndexOffset;
207 type |= minimumType(number: event.m_dataLength) << DataLengthOffset;
208 type |= minimumType(event, length: event.m_dataLength, origBitsPerNumber: event.m_dataType) << DataOffset;
209 stream << type;
210
211 writeNumber(stream, number: event.m_timestamp, type: (type >> TimestampOffset) & TypeMask);
212 writeNumber(stream, number: event.m_typeIndex, type: (type >> TypeIndexOffset) & TypeMask);
213 writeNumber(stream, number: event.m_dataLength, type: (type >> DataLengthOffset) & TypeMask);
214
215 switch ((type >> DataOffset) & TypeMask) {
216 case OneByte:
217 writeNumbers<qint8>(stream, event, length: event.m_dataLength);
218 break;
219 case TwoByte:
220 writeNumbers<qint16>(stream, event, length: event.m_dataLength);
221 break;
222 case FourByte:
223 writeNumbers<qint32>(stream, event, length: event.m_dataLength);
224 break;
225 case EightByte:
226 writeNumbers<qint64>(stream, event, length: event.m_dataLength);
227 break;
228 default:
229 Q_UNREACHABLE();
230 break;
231 }
232
233 return stream;
234}
235
236QT_END_NAMESPACE
237

source code of qtdeclarative/src/qmldebug/qqmlprofilerevent.cpp