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
35class tst_QtEndian: public QObject
36{
37 Q_OBJECT
38
39private 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
60struct 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
73template <typename T> T getData(const TestData &d);
74template <> quint8 getData(const TestData &d) { return d.data8; }
75template <> quint16 getData(const TestData &d) { return d.data16; }
76template <> quint32 getData(const TestData &d) { return d.data32; }
77template <> quint64 getData(const TestData &d) { return d.data64; }
78template <> float getData(const TestData &d) { return d.dataFloat; }
79
80union RawTestData
81{
82 uchar rawData[sizeof(TestData)];
83 TestData data;
84};
85
86template <typename Float>
87Float 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
94static 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};
103static 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};
111static 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
147void tst_QtEndian::fromBigEndian()
148{
149 EXPAND_ENDIAN_TEST(Big);
150}
151
152void tst_QtEndian::fromLittleEndian()
153{
154 EXPAND_ENDIAN_TEST(Little);
155}
156
157#undef ENDIAN_TEST
158
159template <typename T>
160void 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
229void 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
238void 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
249void 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
274void tst_QtEndian::toBigEndian()
275{
276 EXPAND_ENDIAN_TEST(Big);
277}
278
279void tst_QtEndian::toLittleEndian()
280{
281 EXPAND_ENDIAN_TEST(Little);
282}
283
284#undef ENDIAN_TEST
285
286void 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
297void 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
308void 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
319void 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
359void 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
386QTEST_MAIN(tst_QtEndian)
387#include "tst_qtendian.moc"
388

source code of qtbase/tests/auto/corelib/global/qtendian/tst_qtendian.cpp