blob: 2345bb39c107cc61297749f46ab61265fc4405f1 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtCore/qendian.h>
#include <QtCore/private/qendian_p.h>
class tst_QtEndian: public QObject
{
Q_OBJECT
private slots:
void fromBigEndian();
void fromLittleEndian();
void fromBigEndianRegion_data();
void fromBigEndianRegion();
void fromLittleEndianRegion_data() { fromBigEndianRegion_data(); }
void fromLittleEndianRegion();
void toBigEndian();
void toLittleEndian();
void toBigEndianRegion_data() { fromBigEndianRegion_data(); }
void toBigEndianRegion();
void toLittleEndianRegion_data() { fromBigEndianRegion_data(); }
void toLittleEndianRegion();
void endianIntegers_data();
void endianIntegers();
void endianBitfields();
};
struct TestData
{
quint64 data64;
quint32 data32;
quint16 data16;
quint8 data8;
float dataFloat;
double dataDouble;
quint8 reserved;
};
template <typename T> T getData(const TestData &d);
template <> quint8 getData(const TestData &d) { return d.data8; }
template <> quint16 getData(const TestData &d) { return d.data16; }
template <> quint32 getData(const TestData &d) { return d.data32; }
template <> quint64 getData(const TestData &d) { return d.data64; }
template <> float getData(const TestData &d) { return d.dataFloat; }
union RawTestData
{
uchar rawData[sizeof(TestData)];
TestData data;
};
template <typename Float>
Float int2Float(typename QIntegerForSizeof<Float>::Unsigned i)
{
Float result = 0;
memcpy(reinterpret_cast<char *>(&result), reinterpret_cast<const char *>(&i), sizeof (Float));
return result;
}
static const TestData inNativeEndian = {
Q_UINT64_C(0x0123456789abcdef),
0x00c0ffee,
0xcafe,
0xcf,
int2Float<float>(0x00c0ffeeU),
int2Float<double>(Q_UINT64_C(0x0123456789abcdef)),
'\0'
};
static const RawTestData inBigEndian = {
"\x01\x23\x45\x67\x89\xab\xcd\xef"
"\x00\xc0\xff\xee"
"\xca\xfe"
"\xcf"
"\x00\xc0\xff\xee"
"\x01\x23\x45\x67\x89\xab\xcd\xef"
};
static const RawTestData inLittleEndian = {
"\xef\xcd\xab\x89\x67\x45\x23\x01"
"\xee\xff\xc0\x00"
"\xfe\xca"
"\xcf"
"\xee\xff\xc0\x00"
"\xef\xcd\xab\x89\x67\x45\x23\x01"
};
#define EXPAND_ENDIAN_TEST(endian) \
do { \
/* Unsigned tests */ \
ENDIAN_TEST(endian, quint, 64); \
ENDIAN_TEST(endian, quint, 32); \
ENDIAN_TEST(endian, quint, 16); \
ENDIAN_TEST(endian, quint, 8); \
\
/* Signed tests */ \
ENDIAN_TEST(endian, qint, 64); \
ENDIAN_TEST(endian, qint, 32); \
ENDIAN_TEST(endian, qint, 16); \
ENDIAN_TEST(endian, qint, 8); \
} while (false) \
/**/
#define ENDIAN_TEST(endian, type, size) \
do { \
QCOMPARE(qFrom ## endian ## Endian( \
(type ## size)(in ## endian ## Endian.data.data ## size)), \
(type ## size)(inNativeEndian.data ## size)); \
QCOMPARE(qFrom ## endian ## Endian<type ## size>( \
in ## endian ## Endian.rawData + offsetof(TestData, data ## size)), \
(type ## size)(inNativeEndian.data ## size)); \
} while (false) \
/**/
void tst_QtEndian::fromBigEndian()
{
EXPAND_ENDIAN_TEST(Big);
}
void tst_QtEndian::fromLittleEndian()
{
EXPAND_ENDIAN_TEST(Little);
}
#undef ENDIAN_TEST
template <typename T>
void transformRegion_template(T (*transformOne)(T), void (*transformRegion)(const void *, qsizetype, void *))
{
enum { Size = 64 };
T source[Size];
T dest[Size];
T expected = transformOne(getData<T>(inNativeEndian));
std::fill_n(source, +Size, getData<T>(inNativeEndian));
memset(dest, 0, sizeof(dest));
auto checkBounds = [&](int from) {
for ( ; from < Size; ++from)
QCOMPARE(dest[from], T(0));
};
transformRegion(source, 1, dest);
QCOMPARE(dest[0], expected);
checkBounds(1);
memset(dest, 0, sizeof(T));
transformRegion(source, 2, dest);
QCOMPARE(dest[0], expected);
QCOMPARE(dest[1], expected);
checkBounds(2);
memset(dest, 0, sizeof(T) * 2);
transformRegion(source, 3, dest);
QCOMPARE(dest[0], expected);
QCOMPARE(dest[1], expected);
QCOMPARE(dest[2], expected);
checkBounds(3);
memset(dest, 0, sizeof(T) * 3);
transformRegion(source, 4, dest);
QCOMPARE(dest[0], expected);
QCOMPARE(dest[1], expected);
QCOMPARE(dest[2], expected);
QCOMPARE(dest[3], expected);
checkBounds(4);
memset(dest, 0, sizeof(T) * 4);
transformRegion(source, 8, dest);
for (int i = 0; i < 8; ++i)
QCOMPARE(dest[i], expected);
checkBounds(8);
memset(dest, 0, sizeof(T) * 8);
transformRegion(source, 16, dest);
for (int i = 0; i < 16; ++i)
QCOMPARE(dest[i], expected);
checkBounds(16);
memset(dest, 0, sizeof(T) * 16);
transformRegion(source, 32, dest);
for (int i = 0; i < 32; ++i)
QCOMPARE(dest[i], expected);
checkBounds(32);
memset(dest, 0, sizeof(T) * 32);
transformRegion(source, 64, dest);
for (int i = 0; i < 64; ++i)
QCOMPARE(dest[i], expected);
// check transforming in-place
memcpy(dest, source, sizeof(dest));
transformRegion(dest, 64, dest);
for (int i = 0; i < 64; ++i)
QCOMPARE(dest[i], expected);
}
void tst_QtEndian::fromBigEndianRegion_data()
{
QTest::addColumn<int>("size");
QTest::newRow("1") << 1;
QTest::newRow("2") << 2;
QTest::newRow("4") << 4;
QTest::newRow("8") << 8;
}
void tst_QtEndian::fromBigEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qFromBigEndian<quint8>, qFromBigEndian<quint8>);
case 2: return transformRegion_template<quint16>(qFromBigEndian<quint16>, qFromBigEndian<quint16>);
case 4: return transformRegion_template<quint32>(qFromBigEndian<quint32>, qFromBigEndian<quint32>);
case 8: return transformRegion_template<quint64>(qFromBigEndian<quint64>, qFromBigEndian<quint64>);
}
}
void tst_QtEndian::fromLittleEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qFromLittleEndian<quint8>, qFromLittleEndian<quint8>);
case 2: return transformRegion_template<quint16>(qFromLittleEndian<quint16>, qFromLittleEndian<quint16>);
case 4: return transformRegion_template<quint32>(qFromLittleEndian<quint32>, qFromLittleEndian<quint32>);
case 8: return transformRegion_template<quint64>(qFromLittleEndian<quint64>, qFromLittleEndian<quint64>);
}
}
#define ENDIAN_TEST(endian, type, size) \
do { \
QCOMPARE(qTo ## endian ## Endian( \
(type ## size)(inNativeEndian.data ## size)), \
(type ## size)(in ## endian ## Endian.data.data ## size)); \
\
RawTestData test; \
qTo ## endian ## Endian( \
(type ## size)(inNativeEndian.data ## size), \
test.rawData + offsetof(TestData, data ## size)); \
QCOMPARE(test.data.data ## size, in ## endian ## Endian.data.data ## size ); \
} while (false) \
/**/
void tst_QtEndian::toBigEndian()
{
EXPAND_ENDIAN_TEST(Big);
}
void tst_QtEndian::toLittleEndian()
{
EXPAND_ENDIAN_TEST(Little);
}
#undef ENDIAN_TEST
void tst_QtEndian::toBigEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qToBigEndian<quint8>, qToBigEndian<quint8>);
case 2: return transformRegion_template<quint16>(qToBigEndian<quint16>, qToBigEndian<quint16>);
case 4: return transformRegion_template<quint32>(qToBigEndian<quint32>, qToBigEndian<quint32>);
case 8: return transformRegion_template<quint64>(qToBigEndian<quint64>, qToBigEndian<quint64>);
}
}
void tst_QtEndian::toLittleEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qToLittleEndian<quint8>, qToLittleEndian<quint8>);
case 2: return transformRegion_template<quint16>(qToLittleEndian<quint16>, qToLittleEndian<quint16>);
case 4: return transformRegion_template<quint32>(qToLittleEndian<quint32>, qToLittleEndian<quint32>);
case 8: return transformRegion_template<quint64>(qToLittleEndian<quint64>, qToLittleEndian<quint64>);
}
}
void tst_QtEndian::endianIntegers_data()
{
QTest::addColumn<int>("val");
QTest::newRow("-30000") << -30000;
QTest::newRow("-1") << -1;
QTest::newRow("0") << 0;
QTest::newRow("1020") << 1020;
QTest::newRow("16385") << 16385;
}
void tst_QtEndian::endianIntegers()
{
QFETCH(int, val);
qint16 vi16 = val;
qint32 vi32 = val;
qint64 vi64 = val;
quint16 vu16 = val;
quint32 vu32 = val;
quint64 vu64 = val;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), vi16);
QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), vi32);
QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), vi64);
QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), qbswap(vi16));
QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), qbswap(vi32));
QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), qbswap(vi64));
QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), vu16);
QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), vu32);
QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), vu64);
QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), qbswap(vu16));
QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), qbswap(vu32));
QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), qbswap(vu64));
#else
QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), qbswap(vi16));
QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), qbswap(vi32));
QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), qbswap(vi64));
QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), vi16);
QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), vi32);
QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), vi64);
QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), qbswap(vu16));
QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), qbswap(vu32));
QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), qbswap(vu64));
QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), vu16);
QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), vu32);
QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), vu64);
#endif
}
void tst_QtEndian::endianBitfields()
{
union {
quint32_be_bitfield<21, 11> upper;
quint32_be_bitfield<10, 11> lower;
qint32_be_bitfield<0, 10> bottom;
} u;
u.upper = 200;
QCOMPARE(u.upper, 200U);
u.lower = 1000;
u.bottom = -8;
QCOMPARE(u.lower, 1000U);
QCOMPARE(u.upper, 200U);
u.lower += u.upper;
QCOMPARE(u.upper, 200U);
QCOMPARE(u.lower, 1200U);
u.upper = 65536 + 7;
u.lower = 65535;
QCOMPARE(u.lower, 65535U & ((1<<11) - 1));
QCOMPARE(u.upper, 7U);
QCOMPARE(u.bottom, -8);
}
QTEST_MAIN(tst_QtEndian)
#include "tst_qtendian.moc"