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 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | bool 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 | |
18 | bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2) |
19 | { |
20 | return !(event1 == event2); |
21 | } |
22 | |
23 | enum SerializationType { |
24 | OneByte = 0, |
25 | TwoByte = 1, |
26 | FourByte = 2, |
27 | EightByte = 3, |
28 | TypeMask = 0x3 |
29 | }; |
30 | |
31 | enum SerializationTypeOffset { |
32 | TimestampOffset = 0, |
33 | TypeIndexOffset = 2, |
34 | DataLengthOffset = 4, |
35 | DataOffset = 6 |
36 | }; |
37 | |
38 | template<typename Number> |
39 | static inline void readNumbers(QDataStream &stream, Number *data, quint16 length) |
40 | { |
41 | for (quint16 i = 0; i != length; ++i) |
42 | stream >> data[i]; |
43 | } |
44 | |
45 | template<typename Number> |
46 | static 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 | |
74 | QDataStream &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 | |
130 | static 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 | |
162 | template<typename Number> |
163 | static 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 | |
174 | template<typename Number> |
175 | static 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 | |
181 | template<typename Number> |
182 | static 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 | |
203 | QDataStream &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 | |
236 | QT_END_NAMESPACE |
237 | |