| 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 | |