Add qbswap for a memory region

The compiler was generating some vectorized code for qresource.cpp but
it wasn't very efficient. So improve upon it and make use in other
places where we read UTF-16BE strings.

[ChangeLog][QtCore] Added an overload of q{To,From}{Big,Little}Endian
that operates on a memory region.

Change-Id: I6a540578e810472bb455fffd1531fa2f1d724dfc
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
This commit is contained in:
Thiago Macieira 2018-05-25 16:33:48 -03:00
parent c053f9d154
commit d0427759c6
10 changed files with 466 additions and 27 deletions

View File

@ -18,7 +18,7 @@ OBJS = \
#qt code (please keep in order matching DEPEND_SRC)
QOBJS = \
qtextcodec.o qutfcodec.o \
qglobal.o qlogging.o qmalloc.o qnumeric.o qoperatingsystemversion.o qrandom.o \
qendian.o qglobal.o qlogging.o qmalloc.o qnumeric.o qoperatingsystemversion.o qrandom.o \
qabstractfileengine.o qbuffer.o qdatastream.o qdebug.o \
qdir.o qdiriterator.o \
qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \
@ -68,6 +68,7 @@ DEPEND_SRC = \
$(QMKGENSRC)/xmloutput.cpp \
$(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp \
$(SOURCE_PATH)/src/corelib/codecs/qutfcodec.cpp \
$(SOURCE_PATH)/src/corelib/global/qendian.cpp \
$(SOURCE_PATH)/src/corelib/global/qglobal.cpp \
$(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp \
$(SOURCE_PATH)/src/corelib/global/qlogging.cpp \
@ -295,6 +296,9 @@ qdebug.o: $(SOURCE_PATH)/src/corelib/io/qdebug.cpp
qmalloc.o: $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $<
qendian.o: $(SOURCE_PATH)/src/corelib/global/qendian.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $<
qglobal.o: $(SOURCE_PATH)/src/corelib/global/qglobal.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $<

View File

@ -83,6 +83,7 @@ QTOBJS= \
qfsfileengine_win.obj \
qsystemlibrary.obj \
qfileinfo.obj \
qendian.obj \
qglobal.obj \
qhash.obj \
qiodevice.obj \

View File

@ -29,6 +29,7 @@ HEADERS += \
SOURCES += \
global/archdetect.cpp \
global/qendian.cpp \
global/qglobal.cpp \
global/qlibraryinfo.cpp \
global/qmalloc.cpp \

View File

@ -1,11 +1,12 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
@ -14,17 +15,36 @@
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qendian.h"
#include "qalgorithms.h"
#include <private/qsimd_p.h>
QT_BEGIN_NAMESPACE
/*!
\headerfile <QtEndian>
\title Endian Conversion Functions
@ -89,6 +109,32 @@
will return \a src with the byte order swapped; otherwise it will return \a src
unmodified.
*/
/*!
\fn template <typename T> T qFromBigEndian(const void *src, qsizetype count, void *dest)
\since 5.12
\relates <QtEndian>
Reads \a count big-endian numbers from memory location \a src and stores
them in the host byte order representation at \a dest. On CPU architectures
where the host byte order is little-endian (such as x86) this will swap the
byte order; otherwise it will just perform a \c memcpy from \a src to \a
dest.
\note Template type \c{T} can either be a quint16, qint16, quint32, qint32,
quint64, or qint64. Other types of integers, e.g., qlong, are not
applicable.
There are no data alignment constraints for \a src. However, \a dest is
expected to be naturally aligned for type \c{T}.
If \a src and \a dest can be the same pointer, this function will perform
an in-place swap (if necessary). If they are not the same, the memory
regions must not overlap.
\sa qFromLittleEndian()
\sa qToBigEndian()
\sa qToLittleEndian()
*/
/*!
\fn template <typename T> T qFromLittleEndian(const void *src)
\since 4.3
@ -122,6 +168,32 @@
will return \a src with the byte order swapped; otherwise it will return \a src
unmodified.
*/
/*!
\fn template <typename T> T qFromLittleEndian(const void *src, qsizetype count, void *dest)
\since 5.12
\relates <QtEndian>
Reads \a count little-endian numbers from memory location \a src and stores
them in the host byte order representation at \a dest. On CPU architectures
where the host byte order is big-endian (such as PowerPC) this will swap the
byte order; otherwise it will just perform a \c memcpy from \a src to \a
dest.
\note Template type \c{T} can either be a quint16, qint16, quint32, qint32,
quint64, or qint64. Other types of integers, e.g., qlong, are not
applicable.
There are no data alignment constraints for \a src. However, \a dest is
expected to be naturally aligned for type \c{T}.
If \a src and \a dest can be the same pointer, this function will perform
an in-place swap (if necessary). If they are not the same, the memory
regions must not overlap.
\sa qFromLittleEndian()
\sa qToBigEndian()
\sa qToLittleEndian()
*/
/*!
\fn template <typename T> void qToBigEndian(T src, void *dest)
\since 4.3
@ -152,6 +224,32 @@
will return \a src with the byte order swapped; otherwise it will return \a src
unmodified.
*/
/*!
\fn template <typename T> T qToBigEndian(const void *src, qsizetype count, void *dest)
\since 5.12
\relates <QtEndian>
Reads \a count numbers from memory location \a src in the host byte order
and stores them in big-endian representation at \a dest. On CPU
architectures where the host byte order is little-endian (such as x86) this
will swap the byte order; otherwise it will just perform a \c memcpy from
\a src to \a dest.
\note Template type \c{T} can either be a quint16, qint16, quint32, qint32,
quint64, or qint64. Other types of integers, e.g., qlong, are not
applicable.
There are no data alignment constraints for \a dest. However, \a src is
expected to be naturally aligned for type \c{T}.
If \a src and \a dest can be the same pointer, this function will perform
an in-place swap (if necessary). If they are not the same, the memory
regions must not overlap.
\sa qFromLittleEndian()
\sa qToBigEndian()
\sa qToLittleEndian()
*/
/*!
\fn template <typename T> void qToLittleEndian(T src, void *dest)
\since 4.3
@ -182,6 +280,32 @@
will return \a src with the byte order swapped; otherwise it will return \a src
unmodified.
*/
/*!
\fn template <typename T> T qToLittleEndian(const void *src, qsizetype count, void *dest)
\since 5.12
\relates <QtEndian>
Reads \a count numbers from memory location \a src in the host byte order
and stores them in little-endian representation at \a dest. On CPU
architectures where the host byte order is big-endian (such as PowerPC)
this will swap the byte order; otherwise it will just perform a \c memcpy
from \a src to \a dest.
\note Template type \c{T} can either be a quint16, qint16, quint32, qint32,
quint64, or qint64. Other types of integers, e.g., qlong, are not
applicable.
There are no data alignment constraints for \a dest. However, \a src is
expected to be naturally aligned for type \c{T}.
If \a src and \a dest can be the same pointer, this function will perform
an in-place swap (if necessary). If they are not the same, the memory
regions must not overlap.
\sa qFromLittleEndian()
\sa qToBigEndian()
\sa qToLittleEndian()
*/
/*!
\class QLEInteger
@ -552,3 +676,158 @@
\sa qint64
*/
#if defined(__SSSE3__)
using ShuffleMask = uchar[16];
Q_DECL_ALIGN(16) static const ShuffleMask shuffleMasks[3] = {
// 16-bit
{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14},
// 32-bit
{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12},
// 64-bit
{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}
};
static size_t sseSwapLoop(const uchar *src, size_t bytes, uchar *dst,
const __m128i *shuffleMaskPtr) noexcept
{
size_t i = 0;
const __m128i shuffleMask = _mm_load_si128(shuffleMaskPtr);
# ifdef __AVX2__
const __m256i shuffleMask256 = _mm256_inserti128_si256(_mm256_castsi128_si256(shuffleMask), shuffleMask, 1);
for ( ; i + sizeof(__m256i) <= bytes; i += sizeof(__m256i)) {
__m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + i));
data = _mm256_shuffle_epi8(data, shuffleMask256);
_mm256_storeu_si256(reinterpret_cast<__m256i *>(dst + i), data);
}
# else
for ( ; i + 2 * sizeof(__m128i) <= bytes; i += 2 * sizeof(__m128i)) {
__m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + i));
__m128i data2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + i) + 1);
data1 = _mm_shuffle_epi8(data1, shuffleMask);
data2 = _mm_shuffle_epi8(data2, shuffleMask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i), data1);
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i) + 1, data2);
}
# endif
if (i + sizeof(__m128i) <= bytes) {
__m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + i));
data = _mm_shuffle_epi8(data, shuffleMask);
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i), data);
i += sizeof(__m128i);
}
return i;
}
template <typename T> static Q_ALWAYS_INLINE
size_t simdSwapLoop(const uchar *src, size_t bytes, uchar *dst) noexcept
{
auto shuffleMaskPtr = reinterpret_cast<const __m128i *>(shuffleMasks[0]);
shuffleMaskPtr += qCountTrailingZeroBits(sizeof(T)) - 1;
size_t i = sseSwapLoop(src, bytes, dst, shuffleMaskPtr);
// epilogue
for (size_t _i = 0 ; i < bytes && _i < sizeof(__m128i); i += sizeof(T), _i += sizeof(T))
qbswap(qFromUnaligned<T>(src + i), dst + i);
// return the total, so the bswapLoop below does nothing
return bytes;
}
#elif defined(__SSE2__)
template <typename T> static
size_t simdSwapLoop(const uchar *, size_t, uchar *) noexcept
{
// no generic version: we can't do 32- and 64-bit swaps easily,
// so we won't try
return 0;
}
template <> size_t simdSwapLoop<quint16>(const uchar *src, size_t bytes, uchar *dst) noexcept
{
auto swapEndian = [](__m128i &data) {
__m128i lows = _mm_srli_epi16(data, 8);
__m128i highs = _mm_slli_epi16(data, 8);
data = _mm_xor_si128(lows, highs);
};
size_t i = 0;
for ( ; i + 2 * sizeof(__m128i) <= bytes; i += 2 * sizeof(__m128i)) {
__m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + i));
__m128i data2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + i) + 1);
swapEndian(data1);
swapEndian(data2);
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i), data1);
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i) + 1, data2);
}
if (i + sizeof(__m128i) <= bytes) {
__m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + i));
swapEndian(data);
_mm_storeu_si128(reinterpret_cast<__m128i *>(dst + i), data);
i += sizeof(__m128i);
}
// epilogue
for (size_t _i = 0 ; i < bytes && _i < sizeof(__m128i); i += sizeof(quint16), _i += sizeof(quint16))
qbswap(qFromUnaligned<quint16>(src + i), dst + i);
// return the total, so the bswapLoop below does nothing
return bytes;
}
#else
template <typename T> static Q_ALWAYS_INLINE
size_t simdSwapLoop(const uchar *, size_t, uchar *) noexcept
{
return 0;
}
#endif
template <typename T> static Q_ALWAYS_INLINE
void *bswapLoop(const uchar *src, size_t n, uchar *dst) noexcept
{
// Buffers cannot partially overlap: either they're identical or totally
// disjoint (note: they can be adjacent).
if (src != dst) {
quintptr s = quintptr(src);
quintptr d = quintptr(dst);
if (s < d)
Q_ASSERT(s + n <= d);
else
Q_ASSERT(d + n <= s);
}
size_t i = simdSwapLoop<T>(src, n, dst);
for ( ; i < n; i += sizeof(T))
qbswap(qFromUnaligned<T>(src + i), dst + i);
return dst + i;
}
template <> void *qbswap<2>(const void *source, qsizetype n, void *dest) noexcept
{
const uchar *src = reinterpret_cast<const uchar *>(source);
uchar *dst = reinterpret_cast<uchar *>(dest);
return bswapLoop<quint16>(src, n << 1, dst);
}
template <> void *qbswap<4>(const void *source, qsizetype n, void *dest) noexcept
{
const uchar *src = reinterpret_cast<const uchar *>(source);
uchar *dst = reinterpret_cast<uchar *>(dest);
return bswapLoop<quint32>(src, n << 2, dst);
}
template <> void *qbswap<8>(const void *source, qsizetype n, void *dest) noexcept
{
const uchar *src = reinterpret_cast<const uchar *>(source);
uchar *dst = reinterpret_cast<uchar *>(dest);
return bswapLoop<quint64>(src, n << 3, dst);
}
QT_END_NAMESPACE

View File

@ -157,6 +157,14 @@ template <typename T> inline void qbswap(const T src, void *dest)
qToUnaligned<T>(qbswap<T>(src), dest);
}
template <int Size> void *qbswap(const void *source, qsizetype count, void *dest) noexcept;
template<> inline void *qbswap<1>(const void *source, qsizetype count, void *dest) noexcept
{
return source != dest ? memcpy(dest, source, size_t(count)) : dest;
}
template<> Q_CORE_EXPORT void *qbswap<2>(const void *source, qsizetype count, void *dest) noexcept;
template<> Q_CORE_EXPORT void *qbswap<4>(const void *source, qsizetype count, void *dest) noexcept;
template<> Q_CORE_EXPORT void *qbswap<8>(const void *source, qsizetype count, void *dest) noexcept;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
@ -172,6 +180,15 @@ template <typename T> inline void qToBigEndian(T src, void *dest)
{ qToUnaligned<T>(src, dest); }
template <typename T> inline void qToLittleEndian(T src, void *dest)
{ qbswap<T>(src, dest); }
template <typename T> inline void qToBigEndian(const void *source, qsizetype count, void *dest)
{ if (source != dest) memcpy(dest, source, count * sizeof(T)); }
template <typename T> inline void qToLittleEndian(const void *source, qsizetype count, void *dest)
{ qbswap<sizeof(T)>(source, count, dest); }
template <typename T> inline void qFromBigEndian(const void *source, qsizetype count, void *dest)
{ if (source != dest) memcpy(dest, source, count * sizeof(T)); }
template <typename T> inline void qFromLittleEndian(const void *source, qsizetype count, void *dest)
{ qbswap<sizeof(T)>(source, count, dest); }
#else // Q_LITTLE_ENDIAN
template <typename T> inline Q_DECL_CONSTEXPR T qToBigEndian(T source)
@ -187,6 +204,14 @@ template <typename T> inline void qToBigEndian(T src, void *dest)
template <typename T> inline void qToLittleEndian(T src, void *dest)
{ qToUnaligned<T>(src, dest); }
template <typename T> inline void qToBigEndian(const void *source, qsizetype count, void *dest)
{ qbswap<sizeof(T)>(source, count, dest); }
template <typename T> inline void qToLittleEndian(const void *source, qsizetype count, void *dest)
{ if (source != dest) memcpy(dest, source, count * sizeof(T)); }
template <typename T> inline void qFromBigEndian(const void *source, qsizetype count, void *dest)
{ qbswap<sizeof(T)>(source, count, dest); }
template <typename T> inline void qFromLittleEndian(const void *source, qsizetype count, void *dest)
{ if (source != dest) memcpy(dest, source, count * sizeof(T)); }
#endif // Q_BYTE_ORDER == Q_BIG_ENDIAN

View File

@ -52,7 +52,9 @@
#include "qendian.h"
#include <qshareddata.h>
#include <qplatformdefs.h>
#include <qendian.h>
#include "private/qabstractfileengine_p.h"
#include "private/qsimd_p.h"
#include "private/qsystemerror_p.h"
#ifdef Q_OS_UNIX
@ -629,17 +631,13 @@ inline QString QResourceRoot::name(int node) const
QString ret;
qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
const qint16 name_length = qFromBigEndian<qint16>(names + name_offset);
quint16 name_length = qFromBigEndian<qint16>(names + name_offset);
name_offset += 2;
name_offset += 4; //jump past hash
ret.resize(name_length);
QChar *strData = ret.data();
for(int i = 0; i < name_length*2; i+=2) {
QChar c(names[name_offset+i+1], names[name_offset+i]);
*strData = c;
++strData;
}
qFromBigEndian<ushort>(names + name_offset, name_length, strData);
return ret;
}

View File

@ -49,6 +49,7 @@
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
#include "qdatastream.h"
#include "qendian.h"
#include "qfile.h"
#include "qmap.h"
#include "qalgorithms.h"
@ -957,8 +958,8 @@ end:
return QString();
QString str = QString((const QChar *)tn, tn_length/2);
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
for (int i = 0; i < str.length(); ++i)
str[i] = QChar((str.at(i).unicode() >> 8) + ((str.at(i).unicode() << 8) & 0xff00));
QChar *data = str.data();
qbswap<sizeof(QChar)>(data, str.length(), data);
}
return str;
}

View File

@ -9946,11 +9946,7 @@ QDataStream &operator<<(QDataStream &out, const QString &str)
out.writeBytes(reinterpret_cast<const char *>(str.unicode()), sizeof(QChar) * str.length());
} else {
QVarLengthArray<ushort> buffer(str.length());
const ushort *data = reinterpret_cast<const ushort *>(str.constData());
for (int i = 0; i < str.length(); i++) {
buffer[i] = qbswap(*data);
++data;
}
qbswap<sizeof(ushort)>(str.constData(), str.length(), buffer.data());
out.writeBytes(reinterpret_cast<const char *>(buffer.data()), sizeof(ushort) * buffer.size());
}
} else {
@ -10007,10 +10003,7 @@ QDataStream &operator>>(QDataStream &in, QString &str)
if ((in.byteOrder() == QDataStream::BigEndian)
!= (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
ushort *data = reinterpret_cast<ushort *>(str.data());
while (len--) {
*data = qbswap(*data);
++data;
}
qbswap<sizeof(*data)>(data, len, data);
}
} else {
str = QString(QLatin1String(""));

View File

@ -25,6 +25,7 @@ SOURCES += \
../../corelib/codecs/qlatincodec.cpp \
../../corelib/codecs/qtextcodec.cpp \
../../corelib/codecs/qutfcodec.cpp \
../../corelib/global/qendian.cpp \
../../corelib/global/qglobal.cpp \
../../corelib/global/qlogging.cpp \
../../corelib/global/qmalloc.cpp \

View File

@ -39,9 +39,17 @@ class tst_QtEndian: public QObject
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();
@ -59,6 +67,12 @@ struct TestData
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; }
union RawTestData
{
uchar rawData[sizeof(TestData)];
@ -108,6 +122,106 @@ void tst_QtEndian::fromLittleEndian()
#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], 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 { \
@ -135,6 +249,28 @@ void tst_QtEndian::toLittleEndian()
#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");