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:
parent
c053f9d154
commit
d0427759c6
@ -18,7 +18,7 @@ OBJS = \
|
|||||||
#qt code (please keep in order matching DEPEND_SRC)
|
#qt code (please keep in order matching DEPEND_SRC)
|
||||||
QOBJS = \
|
QOBJS = \
|
||||||
qtextcodec.o qutfcodec.o \
|
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 \
|
qabstractfileengine.o qbuffer.o qdatastream.o qdebug.o \
|
||||||
qdir.o qdiriterator.o \
|
qdir.o qdiriterator.o \
|
||||||
qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \
|
qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \
|
||||||
@ -68,6 +68,7 @@ DEPEND_SRC = \
|
|||||||
$(QMKGENSRC)/xmloutput.cpp \
|
$(QMKGENSRC)/xmloutput.cpp \
|
||||||
$(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp \
|
$(SOURCE_PATH)/src/corelib/codecs/qtextcodec.cpp \
|
||||||
$(SOURCE_PATH)/src/corelib/codecs/qutfcodec.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/qglobal.cpp \
|
||||||
$(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp \
|
$(SOURCE_PATH)/src/corelib/global/qlibraryinfo.cpp \
|
||||||
$(SOURCE_PATH)/src/corelib/global/qlogging.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
|
qmalloc.o: $(SOURCE_PATH)/src/corelib/global/qmalloc.cpp
|
||||||
$(CXX) -c -o $@ $(CXXFLAGS) $<
|
$(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
|
qglobal.o: $(SOURCE_PATH)/src/corelib/global/qglobal.cpp
|
||||||
$(CXX) -c -o $@ $(CXXFLAGS) $<
|
$(CXX) -c -o $@ $(CXXFLAGS) $<
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ QTOBJS= \
|
|||||||
qfsfileengine_win.obj \
|
qfsfileengine_win.obj \
|
||||||
qsystemlibrary.obj \
|
qsystemlibrary.obj \
|
||||||
qfileinfo.obj \
|
qfileinfo.obj \
|
||||||
|
qendian.obj \
|
||||||
qglobal.obj \
|
qglobal.obj \
|
||||||
qhash.obj \
|
qhash.obj \
|
||||||
qiodevice.obj \
|
qiodevice.obj \
|
||||||
|
@ -29,6 +29,7 @@ HEADERS += \
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
global/archdetect.cpp \
|
global/archdetect.cpp \
|
||||||
|
global/qendian.cpp \
|
||||||
global/qglobal.cpp \
|
global/qglobal.cpp \
|
||||||
global/qlibraryinfo.cpp \
|
global/qlibraryinfo.cpp \
|
||||||
global/qmalloc.cpp \
|
global/qmalloc.cpp \
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
** Copyright (C) 2018 Intel Corporation.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** 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
|
** Commercial License Usage
|
||||||
** Licensees holding valid commercial Qt licenses may use this file in
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
** accordance with the commercial license agreement provided with the
|
** accordance with the commercial license agreement provided with the
|
||||||
@ -14,17 +15,36 @@
|
|||||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
** information use the contact form at https://www.qt.io/contact-us.
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
**
|
**
|
||||||
** GNU Free Documentation License Usage
|
** GNU Lesser General Public License Usage
|
||||||
** Alternatively, this file may be used under the terms of the GNU Free
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||||
** Documentation License version 1.3 as published by the Free Software
|
** General Public License version 3 as published by the Free Software
|
||||||
** Foundation and appearing in the file included in the packaging of
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||||
** this file. Please review the following information to ensure
|
** packaging of this file. Please review the following information to
|
||||||
** the GNU Free Documentation License version 1.3 requirements
|
** ensure the GNU Lesser General Public License version 3 requirements
|
||||||
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
** 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$
|
** $QT_END_LICENSE$
|
||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "qendian.h"
|
||||||
|
|
||||||
|
#include "qalgorithms.h"
|
||||||
|
#include <private/qsimd_p.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\headerfile <QtEndian>
|
\headerfile <QtEndian>
|
||||||
\title Endian Conversion Functions
|
\title Endian Conversion Functions
|
||||||
@ -89,6 +109,32 @@
|
|||||||
will return \a src with the byte order swapped; otherwise it will return \a src
|
will return \a src with the byte order swapped; otherwise it will return \a src
|
||||||
unmodified.
|
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)
|
\fn template <typename T> T qFromLittleEndian(const void *src)
|
||||||
\since 4.3
|
\since 4.3
|
||||||
@ -122,6 +168,32 @@
|
|||||||
will return \a src with the byte order swapped; otherwise it will return \a src
|
will return \a src with the byte order swapped; otherwise it will return \a src
|
||||||
unmodified.
|
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)
|
\fn template <typename T> void qToBigEndian(T src, void *dest)
|
||||||
\since 4.3
|
\since 4.3
|
||||||
@ -152,6 +224,32 @@
|
|||||||
will return \a src with the byte order swapped; otherwise it will return \a src
|
will return \a src with the byte order swapped; otherwise it will return \a src
|
||||||
unmodified.
|
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)
|
\fn template <typename T> void qToLittleEndian(T src, void *dest)
|
||||||
\since 4.3
|
\since 4.3
|
||||||
@ -182,6 +280,32 @@
|
|||||||
will return \a src with the byte order swapped; otherwise it will return \a src
|
will return \a src with the byte order swapped; otherwise it will return \a src
|
||||||
unmodified.
|
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
|
\class QLEInteger
|
||||||
@ -552,3 +676,158 @@
|
|||||||
|
|
||||||
\sa qint64
|
\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
|
||||||
|
@ -157,6 +157,14 @@ template <typename T> inline void qbswap(const T src, void *dest)
|
|||||||
qToUnaligned<T>(qbswap<T>(src), 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
|
#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); }
|
{ qToUnaligned<T>(src, dest); }
|
||||||
template <typename T> inline void qToLittleEndian(T src, void *dest)
|
template <typename T> inline void qToLittleEndian(T src, void *dest)
|
||||||
{ qbswap<T>(src, 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
|
#else // Q_LITTLE_ENDIAN
|
||||||
|
|
||||||
template <typename T> inline Q_DECL_CONSTEXPR T qToBigEndian(T source)
|
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)
|
template <typename T> inline void qToLittleEndian(T src, void *dest)
|
||||||
{ qToUnaligned<T>(src, 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
|
#endif // Q_BYTE_ORDER == Q_BIG_ENDIAN
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,7 +52,9 @@
|
|||||||
#include "qendian.h"
|
#include "qendian.h"
|
||||||
#include <qshareddata.h>
|
#include <qshareddata.h>
|
||||||
#include <qplatformdefs.h>
|
#include <qplatformdefs.h>
|
||||||
|
#include <qendian.h>
|
||||||
#include "private/qabstractfileengine_p.h"
|
#include "private/qabstractfileengine_p.h"
|
||||||
|
#include "private/qsimd_p.h"
|
||||||
#include "private/qsystemerror_p.h"
|
#include "private/qsystemerror_p.h"
|
||||||
|
|
||||||
#ifdef Q_OS_UNIX
|
#ifdef Q_OS_UNIX
|
||||||
@ -629,17 +631,13 @@ inline QString QResourceRoot::name(int node) const
|
|||||||
|
|
||||||
QString ret;
|
QString ret;
|
||||||
qint32 name_offset = qFromBigEndian<qint32>(tree + offset);
|
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 += 2;
|
||||||
name_offset += 4; //jump past hash
|
name_offset += 4; //jump past hash
|
||||||
|
|
||||||
ret.resize(name_length);
|
ret.resize(name_length);
|
||||||
QChar *strData = ret.data();
|
QChar *strData = ret.data();
|
||||||
for(int i = 0; i < name_length*2; i+=2) {
|
qFromBigEndian<ushort>(names + name_offset, name_length, strData);
|
||||||
QChar c(names[name_offset+i+1], names[name_offset+i]);
|
|
||||||
*strData = c;
|
|
||||||
++strData;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include "qcoreapplication.h"
|
#include "qcoreapplication.h"
|
||||||
#include "qcoreapplication_p.h"
|
#include "qcoreapplication_p.h"
|
||||||
#include "qdatastream.h"
|
#include "qdatastream.h"
|
||||||
|
#include "qendian.h"
|
||||||
#include "qfile.h"
|
#include "qfile.h"
|
||||||
#include "qmap.h"
|
#include "qmap.h"
|
||||||
#include "qalgorithms.h"
|
#include "qalgorithms.h"
|
||||||
@ -957,8 +958,8 @@ end:
|
|||||||
return QString();
|
return QString();
|
||||||
QString str = QString((const QChar *)tn, tn_length/2);
|
QString str = QString((const QChar *)tn, tn_length/2);
|
||||||
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
|
if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
|
||||||
for (int i = 0; i < str.length(); ++i)
|
QChar *data = str.data();
|
||||||
str[i] = QChar((str.at(i).unicode() >> 8) + ((str.at(i).unicode() << 8) & 0xff00));
|
qbswap<sizeof(QChar)>(data, str.length(), data);
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
@ -9946,11 +9946,7 @@ QDataStream &operator<<(QDataStream &out, const QString &str)
|
|||||||
out.writeBytes(reinterpret_cast<const char *>(str.unicode()), sizeof(QChar) * str.length());
|
out.writeBytes(reinterpret_cast<const char *>(str.unicode()), sizeof(QChar) * str.length());
|
||||||
} else {
|
} else {
|
||||||
QVarLengthArray<ushort> buffer(str.length());
|
QVarLengthArray<ushort> buffer(str.length());
|
||||||
const ushort *data = reinterpret_cast<const ushort *>(str.constData());
|
qbswap<sizeof(ushort)>(str.constData(), str.length(), buffer.data());
|
||||||
for (int i = 0; i < str.length(); i++) {
|
|
||||||
buffer[i] = qbswap(*data);
|
|
||||||
++data;
|
|
||||||
}
|
|
||||||
out.writeBytes(reinterpret_cast<const char *>(buffer.data()), sizeof(ushort) * buffer.size());
|
out.writeBytes(reinterpret_cast<const char *>(buffer.data()), sizeof(ushort) * buffer.size());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -10007,10 +10003,7 @@ QDataStream &operator>>(QDataStream &in, QString &str)
|
|||||||
if ((in.byteOrder() == QDataStream::BigEndian)
|
if ((in.byteOrder() == QDataStream::BigEndian)
|
||||||
!= (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
|
!= (QSysInfo::ByteOrder == QSysInfo::BigEndian)) {
|
||||||
ushort *data = reinterpret_cast<ushort *>(str.data());
|
ushort *data = reinterpret_cast<ushort *>(str.data());
|
||||||
while (len--) {
|
qbswap<sizeof(*data)>(data, len, data);
|
||||||
*data = qbswap(*data);
|
|
||||||
++data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
str = QString(QLatin1String(""));
|
str = QString(QLatin1String(""));
|
||||||
|
@ -25,6 +25,7 @@ SOURCES += \
|
|||||||
../../corelib/codecs/qlatincodec.cpp \
|
../../corelib/codecs/qlatincodec.cpp \
|
||||||
../../corelib/codecs/qtextcodec.cpp \
|
../../corelib/codecs/qtextcodec.cpp \
|
||||||
../../corelib/codecs/qutfcodec.cpp \
|
../../corelib/codecs/qutfcodec.cpp \
|
||||||
|
../../corelib/global/qendian.cpp \
|
||||||
../../corelib/global/qglobal.cpp \
|
../../corelib/global/qglobal.cpp \
|
||||||
../../corelib/global/qlogging.cpp \
|
../../corelib/global/qlogging.cpp \
|
||||||
../../corelib/global/qmalloc.cpp \
|
../../corelib/global/qmalloc.cpp \
|
||||||
|
@ -39,9 +39,17 @@ class tst_QtEndian: public QObject
|
|||||||
private slots:
|
private slots:
|
||||||
void fromBigEndian();
|
void fromBigEndian();
|
||||||
void fromLittleEndian();
|
void fromLittleEndian();
|
||||||
|
void fromBigEndianRegion_data();
|
||||||
|
void fromBigEndianRegion();
|
||||||
|
void fromLittleEndianRegion_data() { fromBigEndianRegion_data(); }
|
||||||
|
void fromLittleEndianRegion();
|
||||||
|
|
||||||
void toBigEndian();
|
void toBigEndian();
|
||||||
void toLittleEndian();
|
void toLittleEndian();
|
||||||
|
void toBigEndianRegion_data() { fromBigEndianRegion_data(); }
|
||||||
|
void toBigEndianRegion();
|
||||||
|
void toLittleEndianRegion_data() { fromBigEndianRegion_data(); }
|
||||||
|
void toLittleEndianRegion();
|
||||||
|
|
||||||
void endianIntegers_data();
|
void endianIntegers_data();
|
||||||
void endianIntegers();
|
void endianIntegers();
|
||||||
@ -59,6 +67,12 @@ struct TestData
|
|||||||
quint8 reserved;
|
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
|
union RawTestData
|
||||||
{
|
{
|
||||||
uchar rawData[sizeof(TestData)];
|
uchar rawData[sizeof(TestData)];
|
||||||
@ -108,6 +122,106 @@ void tst_QtEndian::fromLittleEndian()
|
|||||||
|
|
||||||
#undef ENDIAN_TEST
|
#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) \
|
#define ENDIAN_TEST(endian, type, size) \
|
||||||
do { \
|
do { \
|
||||||
@ -135,6 +249,28 @@ void tst_QtEndian::toLittleEndian()
|
|||||||
|
|
||||||
#undef ENDIAN_TEST
|
#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()
|
void tst_QtEndian::endianIntegers_data()
|
||||||
{
|
{
|
||||||
QTest::addColumn<int>("val");
|
QTest::addColumn<int>("val");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user