1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the test suite of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
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 General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
21 | ** included in the packaging of this file. Please review the following |
22 | ** information to ensure the GNU General Public License requirements will |
23 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
24 | ** |
25 | ** $QT_END_LICENSE$ |
26 | ** |
27 | ****************************************************************************/ |
28 | |
29 | |
30 | #include <QtTest/QtTest> |
31 | #include <QtCore/qendian.h> |
32 | #include <QtCore/private/qendian_p.h> |
33 | |
34 | |
35 | class tst_QtEndian: public QObject |
36 | { |
37 | Q_OBJECT |
38 | |
39 | private slots: |
40 | void fromBigEndian(); |
41 | void fromLittleEndian(); |
42 | void fromBigEndianRegion_data(); |
43 | void fromBigEndianRegion(); |
44 | void fromLittleEndianRegion_data() { fromBigEndianRegion_data(); } |
45 | void fromLittleEndianRegion(); |
46 | |
47 | void toBigEndian(); |
48 | void toLittleEndian(); |
49 | void toBigEndianRegion_data() { fromBigEndianRegion_data(); } |
50 | void toBigEndianRegion(); |
51 | void toLittleEndianRegion_data() { fromBigEndianRegion_data(); } |
52 | void toLittleEndianRegion(); |
53 | |
54 | void endianIntegers_data(); |
55 | void endianIntegers(); |
56 | |
57 | void endianBitfields(); |
58 | }; |
59 | |
60 | struct TestData |
61 | { |
62 | quint64 data64; |
63 | quint32 data32; |
64 | quint16 data16; |
65 | quint8 data8; |
66 | |
67 | float dataFloat; |
68 | double dataDouble; |
69 | |
70 | quint8 reserved; |
71 | }; |
72 | |
73 | template <typename T> T getData(const TestData &d); |
74 | template <> quint8 getData(const TestData &d) { return d.data8; } |
75 | template <> quint16 getData(const TestData &d) { return d.data16; } |
76 | template <> quint32 getData(const TestData &d) { return d.data32; } |
77 | template <> quint64 getData(const TestData &d) { return d.data64; } |
78 | template <> float getData(const TestData &d) { return d.dataFloat; } |
79 | |
80 | union RawTestData |
81 | { |
82 | uchar rawData[sizeof(TestData)]; |
83 | TestData data; |
84 | }; |
85 | |
86 | template <typename Float> |
87 | Float int2Float(typename QIntegerForSizeof<Float>::Unsigned i) |
88 | { |
89 | Float result = 0; |
90 | memcpy(dest: reinterpret_cast<char *>(&result), src: reinterpret_cast<const char *>(&i), n: sizeof (Float)); |
91 | return result; |
92 | } |
93 | |
94 | static const TestData inNativeEndian = { |
95 | Q_UINT64_C(0x0123456789abcdef), |
96 | .data32: 0x00c0ffee, |
97 | .data16: 0xcafe, |
98 | .data8: 0xcf, |
99 | .dataFloat: int2Float<float>(i: 0x00c0ffeeU), |
100 | .dataDouble: int2Float<double>(Q_UINT64_C(0x0123456789abcdef)), |
101 | .reserved: '\0' |
102 | }; |
103 | static const RawTestData inBigEndian = { |
104 | .rawData: "\x01\x23\x45\x67\x89\xab\xcd\xef" |
105 | "\x00\xc0\xff\xee" |
106 | "\xca\xfe" |
107 | "\xcf" |
108 | "\x00\xc0\xff\xee" |
109 | "\x01\x23\x45\x67\x89\xab\xcd\xef" |
110 | }; |
111 | static const RawTestData inLittleEndian = { |
112 | .rawData: "\xef\xcd\xab\x89\x67\x45\x23\x01" |
113 | "\xee\xff\xc0\x00" |
114 | "\xfe\xca" |
115 | "\xcf" |
116 | "\xee\xff\xc0\x00" |
117 | "\xef\xcd\xab\x89\x67\x45\x23\x01" |
118 | }; |
119 | |
120 | #define EXPAND_ENDIAN_TEST(endian) \ |
121 | do { \ |
122 | /* Unsigned tests */ \ |
123 | ENDIAN_TEST(endian, quint, 64); \ |
124 | ENDIAN_TEST(endian, quint, 32); \ |
125 | ENDIAN_TEST(endian, quint, 16); \ |
126 | ENDIAN_TEST(endian, quint, 8); \ |
127 | \ |
128 | /* Signed tests */ \ |
129 | ENDIAN_TEST(endian, qint, 64); \ |
130 | ENDIAN_TEST(endian, qint, 32); \ |
131 | ENDIAN_TEST(endian, qint, 16); \ |
132 | ENDIAN_TEST(endian, qint, 8); \ |
133 | } while (false) \ |
134 | /**/ |
135 | |
136 | #define ENDIAN_TEST(endian, type, size) \ |
137 | do { \ |
138 | QCOMPARE(qFrom ## endian ## Endian( \ |
139 | (type ## size)(in ## endian ## Endian.data.data ## size)), \ |
140 | (type ## size)(inNativeEndian.data ## size)); \ |
141 | QCOMPARE(qFrom ## endian ## Endian<type ## size>( \ |
142 | in ## endian ## Endian.rawData + offsetof(TestData, data ## size)), \ |
143 | (type ## size)(inNativeEndian.data ## size)); \ |
144 | } while (false) \ |
145 | /**/ |
146 | |
147 | void tst_QtEndian::fromBigEndian() |
148 | { |
149 | EXPAND_ENDIAN_TEST(Big); |
150 | } |
151 | |
152 | void tst_QtEndian::fromLittleEndian() |
153 | { |
154 | EXPAND_ENDIAN_TEST(Little); |
155 | } |
156 | |
157 | #undef ENDIAN_TEST |
158 | |
159 | template <typename T> |
160 | void transformRegion_template(T (*transformOne)(T), void (*transformRegion)(const void *, qsizetype, void *)) |
161 | { |
162 | enum { Size = 64 }; |
163 | T source[Size]; |
164 | T dest[Size]; |
165 | T expected = transformOne(getData<T>(inNativeEndian)); |
166 | std::fill_n(source, +Size, getData<T>(inNativeEndian)); |
167 | memset(dest, 0, sizeof(dest)); |
168 | |
169 | auto checkBounds = [&](int from) { |
170 | for ( ; from < Size; ++from) |
171 | QCOMPARE(dest[from], T(0)); |
172 | }; |
173 | |
174 | transformRegion(source, 1, dest); |
175 | QCOMPARE(dest[0], expected); |
176 | checkBounds(1); |
177 | memset(dest, 0, sizeof(T)); |
178 | |
179 | transformRegion(source, 2, dest); |
180 | QCOMPARE(dest[0], expected); |
181 | QCOMPARE(dest[1], expected); |
182 | checkBounds(2); |
183 | memset(dest, 0, sizeof(T) * 2); |
184 | |
185 | transformRegion(source, 3, dest); |
186 | QCOMPARE(dest[0], expected); |
187 | QCOMPARE(dest[1], expected); |
188 | QCOMPARE(dest[2], expected); |
189 | checkBounds(3); |
190 | memset(dest, 0, sizeof(T) * 3); |
191 | |
192 | transformRegion(source, 4, dest); |
193 | QCOMPARE(dest[0], expected); |
194 | QCOMPARE(dest[1], expected); |
195 | QCOMPARE(dest[2], expected); |
196 | QCOMPARE(dest[3], expected); |
197 | checkBounds(4); |
198 | memset(dest, 0, sizeof(T) * 4); |
199 | |
200 | transformRegion(source, 8, dest); |
201 | for (int i = 0; i < 8; ++i) |
202 | QCOMPARE(dest[i], expected); |
203 | checkBounds(8); |
204 | memset(dest, 0, sizeof(T) * 8); |
205 | |
206 | transformRegion(source, 16, dest); |
207 | for (int i = 0; i < 16; ++i) |
208 | QCOMPARE(dest[i], expected); |
209 | checkBounds(16); |
210 | memset(dest, 0, sizeof(T) * 16); |
211 | |
212 | transformRegion(source, 32, dest); |
213 | for (int i = 0; i < 32; ++i) |
214 | QCOMPARE(dest[i], expected); |
215 | checkBounds(32); |
216 | memset(dest, 0, sizeof(T) * 32); |
217 | |
218 | transformRegion(source, 64, dest); |
219 | for (int i = 0; i < 64; ++i) |
220 | QCOMPARE(dest[i], expected); |
221 | |
222 | // check transforming in-place |
223 | memcpy(dest, source, sizeof(dest)); |
224 | transformRegion(dest, 64, dest); |
225 | for (int i = 0; i < 64; ++i) |
226 | QCOMPARE(dest[i], expected); |
227 | } |
228 | |
229 | void tst_QtEndian::fromBigEndianRegion_data() |
230 | { |
231 | QTest::addColumn<int>(name: "size" ); |
232 | QTest::newRow(dataTag: "1" ) << 1; |
233 | QTest::newRow(dataTag: "2" ) << 2; |
234 | QTest::newRow(dataTag: "4" ) << 4; |
235 | QTest::newRow(dataTag: "8" ) << 8; |
236 | } |
237 | |
238 | void tst_QtEndian::fromBigEndianRegion() |
239 | { |
240 | QFETCH(int, size); |
241 | switch (size) { |
242 | case 1: return transformRegion_template<quint8>(transformOne: qFromBigEndian<quint8>, transformRegion: qFromBigEndian<quint8>); |
243 | case 2: return transformRegion_template<quint16>(transformOne: qFromBigEndian<quint16>, transformRegion: qFromBigEndian<quint16>); |
244 | case 4: return transformRegion_template<quint32>(transformOne: qFromBigEndian<quint32>, transformRegion: qFromBigEndian<quint32>); |
245 | case 8: return transformRegion_template<quint64>(transformOne: qFromBigEndian<quint64>, transformRegion: qFromBigEndian<quint64>); |
246 | } |
247 | } |
248 | |
249 | void tst_QtEndian::fromLittleEndianRegion() |
250 | { |
251 | QFETCH(int, size); |
252 | switch (size) { |
253 | case 1: return transformRegion_template<quint8>(transformOne: qFromLittleEndian<quint8>, transformRegion: qFromLittleEndian<quint8>); |
254 | case 2: return transformRegion_template<quint16>(transformOne: qFromLittleEndian<quint16>, transformRegion: qFromLittleEndian<quint16>); |
255 | case 4: return transformRegion_template<quint32>(transformOne: qFromLittleEndian<quint32>, transformRegion: qFromLittleEndian<quint32>); |
256 | case 8: return transformRegion_template<quint64>(transformOne: qFromLittleEndian<quint64>, transformRegion: qFromLittleEndian<quint64>); |
257 | } |
258 | } |
259 | |
260 | #define ENDIAN_TEST(endian, type, size) \ |
261 | do { \ |
262 | QCOMPARE(qTo ## endian ## Endian( \ |
263 | (type ## size)(inNativeEndian.data ## size)), \ |
264 | (type ## size)(in ## endian ## Endian.data.data ## size)); \ |
265 | \ |
266 | RawTestData test; \ |
267 | qTo ## endian ## Endian( \ |
268 | (type ## size)(inNativeEndian.data ## size), \ |
269 | test.rawData + offsetof(TestData, data ## size)); \ |
270 | QCOMPARE(test.data.data ## size, in ## endian ## Endian.data.data ## size ); \ |
271 | } while (false) \ |
272 | /**/ |
273 | |
274 | void tst_QtEndian::toBigEndian() |
275 | { |
276 | EXPAND_ENDIAN_TEST(Big); |
277 | } |
278 | |
279 | void tst_QtEndian::toLittleEndian() |
280 | { |
281 | EXPAND_ENDIAN_TEST(Little); |
282 | } |
283 | |
284 | #undef ENDIAN_TEST |
285 | |
286 | void tst_QtEndian::toBigEndianRegion() |
287 | { |
288 | QFETCH(int, size); |
289 | switch (size) { |
290 | case 1: return transformRegion_template<quint8>(transformOne: qToBigEndian<quint8>, transformRegion: qToBigEndian<quint8>); |
291 | case 2: return transformRegion_template<quint16>(transformOne: qToBigEndian<quint16>, transformRegion: qToBigEndian<quint16>); |
292 | case 4: return transformRegion_template<quint32>(transformOne: qToBigEndian<quint32>, transformRegion: qToBigEndian<quint32>); |
293 | case 8: return transformRegion_template<quint64>(transformOne: qToBigEndian<quint64>, transformRegion: qToBigEndian<quint64>); |
294 | } |
295 | } |
296 | |
297 | void tst_QtEndian::toLittleEndianRegion() |
298 | { |
299 | QFETCH(int, size); |
300 | switch (size) { |
301 | case 1: return transformRegion_template<quint8>(transformOne: qToLittleEndian<quint8>, transformRegion: qToLittleEndian<quint8>); |
302 | case 2: return transformRegion_template<quint16>(transformOne: qToLittleEndian<quint16>, transformRegion: qToLittleEndian<quint16>); |
303 | case 4: return transformRegion_template<quint32>(transformOne: qToLittleEndian<quint32>, transformRegion: qToLittleEndian<quint32>); |
304 | case 8: return transformRegion_template<quint64>(transformOne: qToLittleEndian<quint64>, transformRegion: qToLittleEndian<quint64>); |
305 | } |
306 | } |
307 | |
308 | void tst_QtEndian::endianIntegers_data() |
309 | { |
310 | QTest::addColumn<int>(name: "val" ); |
311 | |
312 | QTest::newRow(dataTag: "-30000" ) << -30000; |
313 | QTest::newRow(dataTag: "-1" ) << -1; |
314 | QTest::newRow(dataTag: "0" ) << 0; |
315 | QTest::newRow(dataTag: "1020" ) << 1020; |
316 | QTest::newRow(dataTag: "16385" ) << 16385; |
317 | } |
318 | |
319 | void tst_QtEndian::endianIntegers() |
320 | { |
321 | QFETCH(int, val); |
322 | |
323 | qint16 vi16 = val; |
324 | qint32 vi32 = val; |
325 | qint64 vi64 = val; |
326 | quint16 vu16 = val; |
327 | quint32 vu32 = val; |
328 | quint64 vu64 = val; |
329 | |
330 | #if Q_BYTE_ORDER == Q_BIG_ENDIAN |
331 | QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), vi16); |
332 | QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), vi32); |
333 | QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), vi64); |
334 | QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), qbswap(vi16)); |
335 | QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), qbswap(vi32)); |
336 | QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), qbswap(vi64)); |
337 | QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), vu16); |
338 | QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), vu32); |
339 | QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), vu64); |
340 | QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), qbswap(vu16)); |
341 | QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), qbswap(vu32)); |
342 | QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), qbswap(vu64)); |
343 | #else |
344 | QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), qbswap(vi16)); |
345 | QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), qbswap(vi32)); |
346 | QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), qbswap(vi64)); |
347 | QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), vi16); |
348 | QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), vi32); |
349 | QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), vi64); |
350 | QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), qbswap(vu16)); |
351 | QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), qbswap(vu32)); |
352 | QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), qbswap(vu64)); |
353 | QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), vu16); |
354 | QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), vu32); |
355 | QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), vu64); |
356 | #endif |
357 | } |
358 | |
359 | void tst_QtEndian::endianBitfields() |
360 | { |
361 | union { |
362 | quint32_be_bitfield<21, 11> upper; |
363 | quint32_be_bitfield<10, 11> lower; |
364 | qint32_be_bitfield<0, 10> bottom; |
365 | } u; |
366 | |
367 | u.upper = 200; |
368 | QCOMPARE(u.upper, 200U); |
369 | u.lower = 1000; |
370 | u.bottom = -8; |
371 | QCOMPARE(u.lower, 1000U); |
372 | QCOMPARE(u.upper, 200U); |
373 | |
374 | u.lower += u.upper; |
375 | QCOMPARE(u.upper, 200U); |
376 | QCOMPARE(u.lower, 1200U); |
377 | |
378 | u.upper = 65536 + 7; |
379 | u.lower = 65535; |
380 | QCOMPARE(u.lower, 65535U & ((1<<11) - 1)); |
381 | QCOMPARE(u.upper, 7U); |
382 | |
383 | QCOMPARE(u.bottom, -8); |
384 | } |
385 | |
386 | QTEST_MAIN(tst_QtEndian) |
387 | #include "tst_qtendian.moc" |
388 | |