diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2018-05-25 16:33:48 -0300 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2018-07-04 03:04:40 +0000 |
commit | d0427759c67704fe0f1b04edadd4d30329af268c (patch) | |
tree | be555a72712a68658dd907fc600f03385df8e2cf | |
parent | c053f9d15467459477ccbbe156a096e3ac5e3a91 (diff) |
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>
-rw-r--r-- | qmake/Makefile.unix | 6 | ||||
-rw-r--r-- | qmake/Makefile.win32 | 1 | ||||
-rw-r--r-- | src/corelib/global/global.pri | 1 | ||||
-rw-r--r-- | src/corelib/global/qendian.cpp | 297 | ||||
-rw-r--r-- | src/corelib/global/qendian.h | 25 | ||||
-rw-r--r-- | src/corelib/io/qresource.cpp | 10 | ||||
-rw-r--r-- | src/corelib/kernel/qtranslator.cpp | 5 | ||||
-rw-r--r-- | src/corelib/tools/qstring.cpp | 11 | ||||
-rw-r--r-- | src/tools/bootstrap/bootstrap.pro | 1 | ||||
-rw-r--r-- | tests/auto/corelib/global/qtendian/tst_qtendian.cpp | 136 |
10 files changed, 466 insertions, 27 deletions
diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index 426387f0c2..29461be5c5 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -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) $< diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 index 3abcc1f21c..851185f3ff 100644 --- a/qmake/Makefile.win32 +++ b/qmake/Makefile.win32 @@ -83,6 +83,7 @@ QTOBJS= \ qfsfileengine_win.obj \ qsystemlibrary.obj \ qfileinfo.obj \ + qendian.obj \ qglobal.obj \ qhash.obj \ qiodevice.obj \ diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index 2b4fd6d661..a4d132a4f4 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -29,6 +29,7 @@ HEADERS += \ SOURCES += \ global/archdetect.cpp \ + global/qendian.cpp \ global/qglobal.cpp \ global/qlibraryinfo.cpp \ global/qmalloc.cpp \ diff --git a/src/corelib/global/qendian.cpp b/src/corelib/global/qendian.cpp index 65df25a205..ec6bfec60e 100644 --- a/src/corelib/global/qendian.cpp +++ b/src/corelib/global/qendian.cpp @@ -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 @@ -90,6 +110,32 @@ 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 \relates <QtEndian> @@ -123,6 +169,32 @@ 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 \relates <QtEndian> @@ -153,6 +225,32 @@ 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 \relates <QtEndian> @@ -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 diff --git a/src/corelib/global/qendian.h b/src/corelib/global/qendian.h index a14fce23f8..135bc4460b 100644 --- a/src/corelib/global/qendian.h +++ b/src/corelib/global/qendian.h @@ -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 diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp index 7502fb57a3..367cd78d65 100644 --- a/src/corelib/io/qresource.cpp +++ b/src/corelib/io/qresource.cpp @@ -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; } diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index c868f6d266..23985eb2e9 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -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; } diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index d3a6851e2c..650c3bdb32 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -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("")); diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index d340df8cbb..83e44ff9a4 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -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 \ diff --git a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp index 6934818dcf..89c93bf245 100644 --- a/tests/auto/corelib/global/qtendian/tst_qtendian.cpp +++ b/tests/auto/corelib/global/qtendian/tst_qtendian.cpp @@ -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"); |