1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtQml module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qqmlprofilerevent_p.h" |
41 | #include <QtCore/qdatastream.h> |
42 | |
43 | QT_BEGIN_NAMESPACE |
44 | |
45 | bool operator==(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2) |
46 | { |
47 | if (event1.timestamp() != event2.timestamp() || event1.typeIndex() != event2.typeIndex()) |
48 | return false; |
49 | |
50 | // This is not particularly efficient, but we also don't need to do this very often. |
51 | return event1.numbers<QVarLengthArray<qint64>>() == event2.numbers<QVarLengthArray<qint64>>(); |
52 | } |
53 | |
54 | bool operator!=(const QQmlProfilerEvent &event1, const QQmlProfilerEvent &event2) |
55 | { |
56 | return !(event1 == event2); |
57 | } |
58 | |
59 | enum SerializationType { |
60 | OneByte = 0, |
61 | TwoByte = 1, |
62 | FourByte = 2, |
63 | EightByte = 3, |
64 | TypeMask = 0x3 |
65 | }; |
66 | |
67 | enum SerializationTypeOffset { |
68 | TimestampOffset = 0, |
69 | TypeIndexOffset = 2, |
70 | DataLengthOffset = 4, |
71 | DataOffset = 6 |
72 | }; |
73 | |
74 | template<typename Number> |
75 | static inline void readNumbers(QDataStream &stream, Number *data, quint16 length) |
76 | { |
77 | for (quint16 i = 0; i != length; ++i) |
78 | stream >> data[i]; |
79 | } |
80 | |
81 | template<typename Number> |
82 | static inline Number readNumber(QDataStream &stream, qint8 type) |
83 | { |
84 | switch (type) { |
85 | case OneByte: { |
86 | qint8 value; |
87 | stream >> value; |
88 | return static_cast<Number>(value); |
89 | } |
90 | case TwoByte: { |
91 | qint16 value; |
92 | stream >> value; |
93 | return static_cast<Number>(value); |
94 | } |
95 | case FourByte: { |
96 | qint32 value; |
97 | stream >> value; |
98 | return static_cast<Number>(value); |
99 | } |
100 | case EightByte: { |
101 | qint64 value; |
102 | stream >> value; |
103 | return static_cast<Number>(value); |
104 | } |
105 | default: |
106 | Q_UNREACHABLE(); |
107 | return 0; |
108 | } |
109 | } |
110 | |
111 | QDataStream &operator>>(QDataStream &stream, QQmlProfilerEvent &event) |
112 | { |
113 | qint8 type; |
114 | stream >> type; |
115 | |
116 | event.m_timestamp = readNumber<qint64>(stream, type: (type >> TimestampOffset) & TypeMask); |
117 | event.m_typeIndex = readNumber<qint32>(stream, type: (type >> TypeIndexOffset) & TypeMask); |
118 | event.m_dataLength = readNumber<quint16>(stream, type: (type >> DataLengthOffset) & TypeMask); |
119 | |
120 | uint bytesPerNumber = 1 << ((type >> DataOffset) & TypeMask); |
121 | |
122 | if (event.m_dataLength * bytesPerNumber > sizeof(event.m_data)) { |
123 | event.m_dataType = static_cast<QQmlProfilerEvent::Type>((bytesPerNumber * 8) |
124 | | QQmlProfilerEvent::External); |
125 | event.m_data.external = malloc(size: event.m_dataLength * bytesPerNumber); |
126 | } else { |
127 | event.m_dataType = static_cast<QQmlProfilerEvent::Type>(bytesPerNumber * 8); |
128 | } |
129 | |
130 | switch (event.m_dataType) { |
131 | case QQmlProfilerEvent::Inline8Bit: |
132 | readNumbers<qint8>(stream, data: event.m_data.internal8bit, length: event.m_dataLength); |
133 | break; |
134 | case QQmlProfilerEvent::External8Bit: |
135 | readNumbers<qint8>(stream, data: static_cast<qint8 *>(event.m_data.external), |
136 | length: event.m_dataLength); |
137 | break; |
138 | case QQmlProfilerEvent::Inline16Bit: |
139 | readNumbers<qint16>(stream, data: event.m_data.internal16bit, length: event.m_dataLength); |
140 | break; |
141 | case QQmlProfilerEvent::External16Bit: |
142 | readNumbers<qint16>(stream, data: static_cast<qint16 *>(event.m_data.external), |
143 | length: event.m_dataLength); |
144 | break; |
145 | case QQmlProfilerEvent::Inline32Bit: |
146 | readNumbers<qint32>(stream, data: event.m_data.internal32bit, length: event.m_dataLength); |
147 | break; |
148 | case QQmlProfilerEvent::External32Bit: |
149 | readNumbers<qint32>(stream, data: static_cast<qint32 *>(event.m_data.external), |
150 | length: event.m_dataLength); |
151 | break; |
152 | case QQmlProfilerEvent::Inline64Bit: |
153 | readNumbers<qint64>(stream, data: event.m_data.internal64bit, length: event.m_dataLength); |
154 | break; |
155 | case QQmlProfilerEvent::External64Bit: |
156 | readNumbers<qint64>(stream, data: static_cast<qint64 *>(event.m_data.external), |
157 | length: event.m_dataLength); |
158 | break; |
159 | default: |
160 | Q_UNREACHABLE(); |
161 | break; |
162 | } |
163 | |
164 | return stream; |
165 | } |
166 | |
167 | static inline qint8 minimumType(const QQmlProfilerEvent &event, quint16 length, |
168 | quint16 origBitsPerNumber) |
169 | { |
170 | qint8 type = OneByte; |
171 | bool ok = true; |
172 | for (quint16 i = 0; i < length;) { |
173 | if ((1 << type) == origBitsPerNumber / 8) |
174 | return type; |
175 | switch (type) { |
176 | case OneByte: |
177 | ok = (event.number<qint8>(i) == event.number<qint64>(i)); |
178 | break; |
179 | case TwoByte: |
180 | ok = (event.number<qint16>(i) == event.number<qint64>(i)); |
181 | break; |
182 | case FourByte: |
183 | ok = (event.number<qint32>(i) == event.number<qint64>(i)); |
184 | break; |
185 | default: |
186 | // EightByte isn't possible, as (1 << type) == origBitsPerNumber / 8 then. |
187 | Q_UNREACHABLE(); |
188 | break; |
189 | } |
190 | |
191 | if (ok) |
192 | ++i; |
193 | else |
194 | ++type; |
195 | } |
196 | return type; |
197 | } |
198 | |
199 | template<typename Number> |
200 | static inline qint8 minimumType(Number number) |
201 | { |
202 | if (static_cast<qint8>(number) == number) |
203 | return OneByte; |
204 | if (static_cast<qint16>(number) == number) |
205 | return TwoByte; |
206 | if (static_cast<qint32>(number) == number) |
207 | return FourByte; |
208 | return EightByte; |
209 | } |
210 | |
211 | template<typename Number> |
212 | static inline void writeNumbers(QDataStream &stream, const QQmlProfilerEvent &event, quint16 length) |
213 | { |
214 | for (quint16 i = 0; i != length; ++i) |
215 | stream << event.number<Number>(i); |
216 | } |
217 | |
218 | template<typename Number> |
219 | static inline void writeNumber(QDataStream &stream, Number number, qint8 type) |
220 | { |
221 | switch (type) { |
222 | case OneByte: |
223 | stream << static_cast<qint8>(number); |
224 | break; |
225 | case TwoByte: |
226 | stream << static_cast<qint16>(number); |
227 | break; |
228 | case FourByte: |
229 | stream << static_cast<qint32>(number); |
230 | break; |
231 | case EightByte: |
232 | stream << static_cast<qint64>(number); |
233 | break; |
234 | default: |
235 | Q_UNREACHABLE(); |
236 | break; |
237 | } |
238 | } |
239 | |
240 | QDataStream &operator<<(QDataStream &stream, const QQmlProfilerEvent &event) |
241 | { |
242 | qint8 type = minimumType(number: event.m_timestamp); // << TimestampOffset; |
243 | type |= minimumType(number: event.m_typeIndex) << TypeIndexOffset; |
244 | type |= minimumType(number: event.m_dataLength) << DataLengthOffset; |
245 | type |= minimumType(event, length: event.m_dataLength, origBitsPerNumber: event.m_dataType) << DataOffset; |
246 | stream << type; |
247 | |
248 | writeNumber(stream, number: event.m_timestamp, type: (type >> TimestampOffset) & TypeMask); |
249 | writeNumber(stream, number: event.m_typeIndex, type: (type >> TypeIndexOffset) & TypeMask); |
250 | writeNumber(stream, number: event.m_dataLength, type: (type >> DataLengthOffset) & TypeMask); |
251 | |
252 | switch ((type >> DataOffset) & TypeMask) { |
253 | case OneByte: |
254 | writeNumbers<qint8>(stream, event, length: event.m_dataLength); |
255 | break; |
256 | case TwoByte: |
257 | writeNumbers<qint16>(stream, event, length: event.m_dataLength); |
258 | break; |
259 | case FourByte: |
260 | writeNumbers<qint32>(stream, event, length: event.m_dataLength); |
261 | break; |
262 | case EightByte: |
263 | writeNumbers<qint64>(stream, event, length: event.m_dataLength); |
264 | break; |
265 | default: |
266 | Q_UNREACHABLE(); |
267 | break; |
268 | } |
269 | |
270 | return stream; |
271 | } |
272 | |
273 | QT_END_NAMESPACE |
274 | |