diff options
Diffstat (limited to 'src/corelib/tools')
30 files changed, 1415 insertions, 622 deletions
diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index f0cc56e899..a642fb9b39 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -159,6 +159,7 @@ struct QTypedArrayData inline iterator &operator-=(int j) { i-=j; return *this; } inline iterator operator+(int j) const { return iterator(i+j); } inline iterator operator-(int j) const { return iterator(i-j); } + friend inline iterator operator+(int j, iterator k) { return k + j; } inline int operator-(iterator j) const { return i - j.i; } inline operator T*() const { return i; } }; @@ -194,6 +195,7 @@ struct QTypedArrayData inline const_iterator &operator-=(int j) { i-=j; return *this; } inline const_iterator operator+(int j) const { return const_iterator(i+j); } inline const_iterator operator-(int j) const { return const_iterator(i-j); } + friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } inline int operator-(const_iterator j) const { return i - j.i; } inline operator const T*() const { return i; } }; diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 424420204a..d8d8be7a26 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -47,6 +47,7 @@ #include "qlocale_p.h" #include "qlocale_tools_p.h" #include "private/qnumeric_p.h" +#include "private/qsimd_p.h" #include "qstringalgorithms_p.h" #include "qscopedpointer.h" #include "qbytearray_p.h" @@ -356,7 +357,8 @@ char *qstrncpy(char *dst, const char *src, uint len) Special case 2: Returns an arbitrary non-zero value if \a str1 is nullptr or \a str2 is nullptr (but not both). - \sa qstrncmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons} + \sa qstrncmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons}, + QByteArray::compare() */ int qstrcmp(const char *str1, const char *str2) { @@ -381,7 +383,8 @@ int qstrcmp(const char *str1, const char *str2) Special case 2: Returns a random non-zero value if \a str1 is nullptr or \a str2 is nullptr (but not both). - \sa qstrcmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons} + \sa qstrcmp(), qstricmp(), qstrnicmp(), {8-bit Character Comparisons}, + QByteArray::compare() */ /*! \relates QByteArray @@ -400,21 +403,80 @@ int qstrcmp(const char *str1, const char *str2) Special case 2: Returns a random non-zero value if \a str1 is nullptr or \a str2 is nullptr (but not both). - \sa qstrcmp(), qstrncmp(), qstrnicmp(), {8-bit Character Comparisons} + \sa qstrcmp(), qstrncmp(), qstrnicmp(), {8-bit Character Comparisons}, + QByteArray::compare() */ int qstricmp(const char *str1, const char *str2) { const uchar *s1 = reinterpret_cast<const uchar *>(str1); const uchar *s2 = reinterpret_cast<const uchar *>(str2); - int res; - uchar c; - if (!s1 || !s2) - return s1 ? 1 : (s2 ? -1 : 0); - for (; !(res = (c = latin1_lowercased[*s1]) - latin1_lowercased[*s2]); s1++, s2++) - if (!c) // strings are equal - break; - return res; + if (!s1) + return s2 ? -1 : 0; + if (!s2) + return 1; + + enum { Incomplete = 256 }; + qptrdiff offset = 0; + auto innerCompare = [=, &offset](qptrdiff max, bool unlimited) { + max += offset; + do { + uchar c = latin1_lowercased[s1[offset]]; + int res = c - latin1_lowercased[s2[offset]]; + if (Q_UNLIKELY(res)) + return res; + if (Q_UNLIKELY(!c)) + return 0; + ++offset; + } while (unlimited || offset < max); + return int(Incomplete); + }; + +#ifdef __SSE4_1__ + enum { PageSize = 4096, PageMask = PageSize - 1 }; + const __m128i zero = _mm_setzero_si128(); + forever { + // Calculate how many bytes we can load until we cross a page boundary + // for either source. This isn't an exact calculation, just something + // very quick. + quintptr u1 = quintptr(s1 + offset); + quintptr u2 = quintptr(s2 + offset); + uint n = PageSize - ((u1 | u2) & PageMask); + + qptrdiff maxoffset = offset + n; + for ( ; offset + 16 <= maxoffset; offset += sizeof(__m128i)) { + // load 16 bytes from either source + __m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s1 + offset)); + __m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i *>(s2 + offset)); + + // compare the two against each oher + __m128i cmp = _mm_cmpeq_epi8(a, b); + + // find NUL terminators too + cmp = _mm_min_epu8(cmp, a); + cmp = _mm_cmpeq_epi8(cmp, zero); + + // was there any difference or a NUL? + uint mask = _mm_movemask_epi8(cmp); + if (mask) { + // yes, find out where + uint start = qCountTrailingZeroBits(mask); + uint end = sizeof(mask) * 8 - qCountLeadingZeroBits(mask); + Q_ASSUME(end >= start); + offset += start; + n = end - start; + break; + } + } + + // using SIMD could cause a page fault, so iterate byte by byte + int res = innerCompare(n, false); + if (res != Incomplete) + return res; + } +#endif + + return innerCompare(-1, true); } /*! \relates QByteArray @@ -434,7 +496,8 @@ int qstricmp(const char *str1, const char *str2) Special case 2: Returns a random non-zero value if \a str1 is nullptr or \a str2 is nullptr (but not both). - \sa qstrcmp(), qstrncmp(), qstricmp(), {8-bit Character Comparisons} + \sa qstrcmp(), qstrncmp(), qstricmp(), {8-bit Character Comparisons}, + QByteArray::compare() */ int qstrnicmp(const char *str1, const char *str2, uint len) @@ -456,6 +519,55 @@ int qstrnicmp(const char *str1, const char *str2, uint len) /*! \internal + \since 5.12 + + A helper for QByteArray::compare. Compares \a len1 bytes from \a str1 to \a + len2 bytes from \a str2. If \a len2 is -1, then \a str2 is expected to be + null-terminated. + */ +int qstrnicmp(const char *str1, qsizetype len1, const char *str2, qsizetype len2) +{ + Q_ASSERT(str1); + Q_ASSERT(len1 >= 0); + Q_ASSERT(len2 >= -1); + const uchar *s1 = reinterpret_cast<const uchar *>(str1); + const uchar *s2 = reinterpret_cast<const uchar *>(str2); + if (!s2) + return len1 == 0 ? 0 : 1; + + int res; + uchar c; + if (len2 == -1) { + // null-terminated str2 + qsizetype i; + for (i = 0; i < len1; ++i) { + c = latin1_lowercased[s2[i]]; + if (!c) + return 1; + + res = latin1_lowercased[s1[i]] - c; + if (res) + return res; + } + c = latin1_lowercased[s2[i]]; + return c ? -1 : 0; + } else { + // not null-terminated + for (qsizetype i = 0; i < qMin(len1, len2); ++i) { + c = latin1_lowercased[s2[i]]; + res = latin1_lowercased[s1[i]] - c; + if (res) + return res; + } + if (len1 == len2) + return 0; + return len1 < len2 ? -1 : 1; + } +} + +/*! + \internal + ### Qt6: replace the QByteArray parameter with [pointer,len] pair */ int qstrcmp(const QByteArray &str1, const char *str2) { @@ -483,6 +595,7 @@ int qstrcmp(const QByteArray &str1, const char *str2) /*! \internal + ### Qt6: replace the QByteArray parameter with [pointer,len] pair */ int qstrcmp(const QByteArray &str1, const QByteArray &str2) { @@ -950,7 +1063,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) $LC_CTYPE is set, most Unix systems do "the right thing".) Functions that this affects include contains(), indexOf(), lastIndexOf(), operator<(), operator<=(), operator>(), - operator>=(), toLower() and toUpper(). + operator>=(), isLower(), isUpper(), toLower() and toUpper(). This issue does not apply to \l{QString}s since they represent characters using Unicode. @@ -2864,6 +2977,31 @@ int QByteArray::count(char ch) const */ /*! + \fn int QByteArray::compare(const char *c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + \since 5.12 + + Returns an integer less than, equal to, or greater than zero depending on + whether this QByteArray sorts before, at the same position, or after the + string pointed to by \a c. The comparison is performed according to case + sensitivity \a cs. + + \sa operator== +*/ + +/*! + \fn int QByteArray::compare(const QByteArray &a, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + \overload + \since 5.12 + + Returns an integer less than, equal to, or greater than zero depending on + whether this QByteArray sorts before, at the same position, or after the + QByteArray \a a. The comparison is performed according to case sensitivity + \a cs. + + \sa operator== +*/ + +/*! Returns \c true if this byte array starts with byte array \a ba; otherwise returns \c false. @@ -2941,6 +3079,78 @@ bool QByteArray::endsWith(const char *str) const return qstrncmp(d->data() + d->size - len, str, len) == 0; } +/*! + Returns true if \a c is an uppercase Latin1 letter. + \note The multiplication sign 0xD7 and the sz ligature 0xDF are not + treated as uppercase Latin1. + */ +static inline bool isUpperCaseLatin1(char c) +{ + if (c >= 'A' && c <= 'Z') + return true; + + return (uchar(c) >= 0xC0 && uchar(c) <= 0xDE && uchar(c) != 0xD7); +} + +/*! + Returns \c true if this byte array contains only uppercase letters, + otherwise returns \c false. The byte array is interpreted as a Latin-1 + encoded string. + \since 5.12 + + \sa isLower(), toUpper() +*/ +bool QByteArray::isUpper() const +{ + if (isEmpty()) + return false; + + const char *d = data(); + + for (int i = 0, max = size(); i < max; ++i) { + if (!isUpperCaseLatin1(d[i])) + return false; + } + + return true; +} + +/*! + Returns true if \a c is an lowercase Latin1 letter. + \note The division sign 0xF7 is not treated as lowercase Latin1, + but the small y dieresis 0xFF is. + */ +static inline bool isLowerCaseLatin1(char c) +{ + if (c >= 'a' && c <= 'z') + return true; + + return (uchar(c) >= 0xD0 && uchar(c) != 0xF7); +} + +/*! + Returns \c true if this byte array contains only lowercase letters, + otherwise returns \c false. The byte array is interpreted as a Latin-1 + encoded string. + \since 5.12 + + \sa isUpper(), toLower() + */ +bool QByteArray::isLower() const +{ + if (isEmpty()) + return false; + + const char *d = data(); + + for (int i = 0, max = size(); i < max; ++i) { + if (!isLowerCaseLatin1(d[i])) + return false; + } + + return true; +} + /*! \overload Returns \c true if this byte array ends with character \a ch; @@ -3052,7 +3262,7 @@ QByteArray QByteArray::mid(int pos, int len) const Example: \snippet code/src_corelib_tools_qbytearray.cpp 30 - \sa toUpper(), {8-bit Character Comparisons} + \sa isLower(), toUpper(), {8-bit Character Comparisons} */ // prevent the compiler from inlining the function in each of @@ -3106,7 +3316,7 @@ QByteArray QByteArray::toLower_helper(QByteArray &a) Example: \snippet code/src_corelib_tools_qbytearray.cpp 31 - \sa toLower(), {8-bit Character Comparisons} + \sa isUpper(), toLower(), {8-bit Character Comparisons} */ QByteArray QByteArray::toUpper_helper(const QByteArray &a) @@ -3295,6 +3505,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator==(const QByteArray &a1, const char *a2) @@ -3304,6 +3516,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is equal to string \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator==(const char *a1, const QByteArray &a2) @@ -3313,6 +3527,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if string \a a1 is equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator!=(const QByteArray &a1, const QByteArray &a2) @@ -3322,6 +3538,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is not equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator!=(const QByteArray &a1, const char *a2) @@ -3331,6 +3549,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is not equal to string \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator!=(const char *a1, const QByteArray &a2) @@ -3340,6 +3560,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if string \a a1 is not equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator<(const QByteArray &a1, const QByteArray &a2) @@ -3349,6 +3571,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically less than byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn inline bool operator<(const QByteArray &a1, const char *a2) @@ -3358,6 +3582,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically less than string \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator<(const char *a1, const QByteArray &a2) @@ -3367,6 +3593,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if string \a a1 is lexically less than byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator<=(const QByteArray &a1, const QByteArray &a2) @@ -3376,6 +3604,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically less than or equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator<=(const QByteArray &a1, const char *a2) @@ -3385,6 +3615,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically less than or equal to string \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator<=(const char *a1, const QByteArray &a2) @@ -3394,6 +3626,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if string \a a1 is lexically less than or equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator>(const QByteArray &a1, const QByteArray &a2) @@ -3403,6 +3637,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically greater than byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator>(const QByteArray &a1, const char *a2) @@ -3412,6 +3648,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically greater than string \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator>(const char *a1, const QByteArray &a2) @@ -3421,6 +3659,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if string \a a1 is lexically greater than byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator>=(const QByteArray &a1, const QByteArray &a2) @@ -3430,6 +3670,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically greater than or equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator>=(const QByteArray &a1, const char *a2) @@ -3439,6 +3681,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if byte array \a a1 is lexically greater than or equal to string \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn bool operator>=(const char *a1, const QByteArray &a2) @@ -3448,6 +3692,8 @@ QDataStream &operator>>(QDataStream &in, QByteArray &ba) Returns \c true if string \a a1 is lexically greater than or equal to byte array \a a2; otherwise returns \c false. + + \sa QByteArray::compare() */ /*! \fn const QByteArray operator+(const QByteArray &a1, const QByteArray &a2) diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 300f795469..8ee3a29ecc 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -99,6 +99,7 @@ inline int qstrncmp(const char *str1, const char *str2, uint len) } Q_CORE_EXPORT int qstricmp(const char *, const char *); Q_CORE_EXPORT int qstrnicmp(const char *, const char *, uint len); +Q_CORE_EXPORT int qstrnicmp(const char *, qsizetype, const char *, qsizetype = -1); // implemented in qvsnprintf.cpp Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap); @@ -231,6 +232,9 @@ public: int count(const char *a) const; int count(const QByteArray &a) const; + inline int compare(const char *c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + inline int compare(const QByteArray &a, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + Q_REQUIRED_RESULT QByteArray left(int len) const; Q_REQUIRED_RESULT QByteArray right(int len) const; Q_REQUIRED_RESULT QByteArray mid(int index, int len = -1) const; @@ -245,6 +249,9 @@ public: bool endsWith(char c) const; bool endsWith(const char *c) const; + bool isUpper() const; + bool isLower() const; + void truncate(int pos); void chop(int n); @@ -600,6 +607,16 @@ inline bool QByteArray::contains(const QByteArray &a) const { return indexOf(a) != -1; } inline bool QByteArray::contains(char c) const { return indexOf(c) != -1; } +inline int QByteArray::compare(const char *c, Qt::CaseSensitivity cs) const +{ + return cs == Qt::CaseSensitive ? qstrcmp(*this, c) : + qstrnicmp(data(), size(), c, -1); +} +inline int QByteArray::compare(const QByteArray &a, Qt::CaseSensitivity cs) const +{ + return cs == Qt::CaseSensitive ? qstrcmp(*this, a) : + qstrnicmp(data(), size(), a.data(), a.size()); +} inline bool operator==(const QByteArray &a1, const QByteArray &a2) Q_DECL_NOTHROW { return (a1.size() == a2.size()) && (memcmp(a1.constData(), a2.constData(), a1.size())==0); } inline bool operator==(const QByteArray &a1, const char *a2) Q_DECL_NOTHROW diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h index 18facf7e42..faa7263d6b 100644 --- a/src/corelib/tools/qcontiguouscache.h +++ b/src/corelib/tools/qcontiguouscache.h @@ -211,6 +211,7 @@ void QContiguousCache<T>::detach_helper() template <typename T> void QContiguousCache<T>::setCapacity(int asize) { + Q_ASSERT(asize >= 0); if (asize == d->alloc) return; detach(); @@ -285,6 +286,7 @@ inline QContiguousCacheData *QContiguousCache<T>::allocateData(int aalloc) template <typename T> QContiguousCache<T>::QContiguousCache(int cap) { + Q_ASSERT(cap >= 0); d = allocateData(cap); d->ref.store(1); d->alloc = cap; diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index a1b121f1ee..3c79bb797d 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -544,6 +544,46 @@ QByteArray QCryptographicHash::hash(const QByteArray &data, Algorithm method) return hash.result(); } +/*! + Returns the size of the output of the selected hash \a method in bytes. + + \since 5.12 +*/ +int QCryptographicHash::hashLength(QCryptographicHash::Algorithm method) +{ + switch (method) { + case QCryptographicHash::Sha1: + return 20; +#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 + case QCryptographicHash::Md4: + return 16; + case QCryptographicHash::Md5: + return 16; + case QCryptographicHash::Sha224: + return SHA224HashSize; + case QCryptographicHash::Sha256: + return SHA256HashSize; + case QCryptographicHash::Sha384: + return SHA384HashSize; + case QCryptographicHash::Sha512: + return SHA512HashSize; + case QCryptographicHash::RealSha3_224: + case QCryptographicHash::Keccak_224: + return 224 / 8; + case QCryptographicHash::RealSha3_256: + case QCryptographicHash::Keccak_256: + return 256 / 8; + case QCryptographicHash::RealSha3_384: + case QCryptographicHash::Keccak_384: + return 384 / 8; + case QCryptographicHash::RealSha3_512: + case QCryptographicHash::Keccak_512: + return 512 / 8; +#endif + } + return 0; +} + QT_END_NAMESPACE #ifndef QT_NO_QOBJECT diff --git a/src/corelib/tools/qcryptographichash.h b/src/corelib/tools/qcryptographichash.h index 2f74d42405..ad1de7c756 100644 --- a/src/corelib/tools/qcryptographichash.h +++ b/src/corelib/tools/qcryptographichash.h @@ -101,6 +101,7 @@ public: QByteArray result() const; static QByteArray hash(const QByteArray &data, Algorithm method); + static int hashLength(Algorithm method); private: Q_DISABLE_COPY(QCryptographicHash) QCryptographicHashPrivate *d; diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index ce663ce2ca..a586ca5671 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -348,6 +348,7 @@ public: inline iterator operator-(int j) const { return operator+(-j); } inline iterator &operator+=(int j) { return *this = *this + j; } inline iterator &operator-=(int j) { return *this = *this - j; } + friend inline iterator operator+(int j, iterator k) { return k + j; } #ifndef QT_STRICT_ITERATORS public: @@ -413,6 +414,7 @@ public: inline const_iterator operator-(int j) const { return operator+(-j); } inline const_iterator &operator+=(int j) { return *this = *this + j; } inline const_iterator &operator-=(int j) { return *this = *this - j; } + friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } // ### Qt 5: not sure this is necessary anymore #ifdef QT_STRICT_ITERATORS diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index c8f3f4c8c3..1e6d4df474 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -159,6 +159,7 @@ public: inline iterator operator-(int j) const { return operator+(-j); } inline iterator &operator+=(int j) { return *this = *this + j; } inline iterator &operator-=(int j) { return *this = *this - j; } + friend inline iterator operator+(int j, iterator k) { return k + j; } }; friend class iterator; @@ -193,6 +194,7 @@ public: inline const_iterator operator-(int j) const { return operator+(-j); } inline const_iterator &operator+=(int j) { return *this = *this + j; } inline const_iterator &operator-=(int j) { return *this = *this - j; } + friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } }; friend class const_iterator; diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index af7659e995..c00220ad3a 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -269,6 +269,7 @@ public: inline iterator &operator-=(difference_type j) { i-=j; return *this; } inline iterator operator+(difference_type j) const { return iterator(i+j); } inline iterator operator-(difference_type j) const { return iterator(i-j); } + friend inline iterator operator+(difference_type j, iterator k) { return k + j; } inline int operator-(iterator j) const { return int(i - j.i); } }; friend class iterator; @@ -312,6 +313,7 @@ public: inline const_iterator &operator-=(difference_type j) { i-=j; return *this; } inline const_iterator operator+(difference_type j) const { return const_iterator(i+j); } inline const_iterator operator-(difference_type j) const { return const_iterator(i-j); } + friend inline const_iterator operator+(difference_type j, const_iterator k) { return k + j; } inline int operator-(const_iterator j) const { return int(i - j.i); } }; friend class const_iterator; diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index 00a2c05c35..e70630eb12 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -233,14 +233,6 @@ QLocaleId QLocaleId::withLikelySubtagsAdded() const if (addLikelySubtags(id)) return id; } - // language_script - if (country_id) { - QLocaleId id = QLocaleId::fromIds(language_id, script_id, 0); - if (addLikelySubtags(id)) { - id.country_id = country_id; - return id; - } - } // language_region if (script_id) { QLocaleId id = QLocaleId::fromIds(language_id, 0, country_id); @@ -249,6 +241,14 @@ QLocaleId QLocaleId::withLikelySubtagsAdded() const return id; } } + // language_script + if (country_id) { + QLocaleId id = QLocaleId::fromIds(language_id, script_id, 0); + if (addLikelySubtags(id)) { + id.country_id = country_id; + return id; + } + } // language if (script_id && country_id) { QLocaleId id = QLocaleId::fromIds(language_id, 0, 0); @@ -258,6 +258,14 @@ QLocaleId QLocaleId::withLikelySubtagsAdded() const return id; } } + // und_script + if (language_id) { + QLocaleId id = QLocaleId::fromIds(0, script_id, 0); + if (addLikelySubtags(id)) { + id.language_id = language_id; + return id; + } + } return *this; } @@ -382,6 +390,13 @@ const QLocaleData *QLocaleData::findLocaleData(QLocale::Language language, QLoca QList<QLocaleId> tried; tried.push_back(likelyId); + // No match; try again with raw data: + if (!tried.contains(localeId)) { + if (const QLocaleData *const data = findLocaleDataById(localeId)) + return data; + tried.push_back(localeId); + } + // No match; try again with likely country if (country != QLocale::AnyCountry && (language != QLocale::AnyLanguage || script != QLocale::AnyScript)) { diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index a5b9096835..1cf9299e26 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -449,6 +449,7 @@ public: inline iterator operator-(int j) const { return operator+(-j); } inline iterator &operator+=(int j) { return *this = *this + j; } inline iterator &operator-=(int j) { return *this = *this - j; } + friend inline iterator operator+(int j, iterator k) { return k + j; } #ifndef QT_STRICT_ITERATORS public: @@ -512,6 +513,7 @@ public: inline const_iterator operator-(int j) const { return operator+(-j); } inline const_iterator &operator+=(int j) { return *this = *this + j; } inline const_iterator &operator-=(int j) { return *this = *this - j; } + friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } #ifdef QT_STRICT_ITERATORS private: diff --git a/src/corelib/tools/qregexp.cpp b/src/corelib/tools/qregexp.cpp index 96ddca56af..87b30c952e 100644 --- a/src/corelib/tools/qregexp.cpp +++ b/src/corelib/tools/qregexp.cpp @@ -3813,50 +3813,57 @@ struct QRegExpPrivate }; #if !defined(QT_NO_REGEXP_OPTIM) -typedef QCache<QRegExpEngineKey, QRegExpEngine> EngineCache; -Q_GLOBAL_STATIC(EngineCache, globalEngineCache) -static QBasicMutex globalEngineCacheMutex; +struct QRECache +{ + typedef QHash<QRegExpEngineKey, QRegExpEngine *> EngineCache; + typedef QCache<QRegExpEngineKey, QRegExpEngine> UnusedEngineCache; + EngineCache usedEngines; + UnusedEngineCache unusedEngines; +}; +Q_GLOBAL_STATIC(QRECache, engineCache) +static QBasicMutex engineCacheMutex; #endif // QT_NO_REGEXP_OPTIM static void derefEngine(QRegExpEngine *eng, const QRegExpEngineKey &key) { - if (!eng->ref.deref()) { #if !defined(QT_NO_REGEXP_OPTIM) - if (globalEngineCache()) { - QMutexLocker locker(&globalEngineCacheMutex); - QT_TRY { - globalEngineCache()->insert(key, eng, 4 + key.pattern.length() / 4); - } QT_CATCH(const std::bad_alloc &) { - // in case of an exception (e.g. oom), just delete the engine - delete eng; - } + QMutexLocker locker(&engineCacheMutex); + if (!eng->ref.deref()) { + if (QRECache *c = engineCache()) { + c->unusedEngines.insert(key, eng, 4 + key.pattern.length() / 4); + c->usedEngines.remove(key); } else { delete eng; } + } #else - Q_UNUSED(key); + Q_UNUSED(key); + if (!eng->ref.deref()) delete eng; #endif - } } static void prepareEngine_helper(QRegExpPrivate *priv) { - bool initMatchState = !priv->eng; + Q_ASSERT(!priv->eng); + #if !defined(QT_NO_REGEXP_OPTIM) - if (!priv->eng && globalEngineCache()) { - QMutexLocker locker(&globalEngineCacheMutex); - priv->eng = globalEngineCache()->take(priv->engineKey); - if (priv->eng != 0) + QMutexLocker locker(&engineCacheMutex); + if (QRECache *c = engineCache()) { + priv->eng = c->unusedEngines.take(priv->engineKey); + if (!priv->eng) + priv->eng = c->usedEngines.value(priv->engineKey); + if (!priv->eng) + priv->eng = new QRegExpEngine(priv->engineKey); + else priv->eng->ref.ref(); + + c->usedEngines.insert(priv->engineKey, priv->eng); + return; } #endif // QT_NO_REGEXP_OPTIM - if (!priv->eng) - priv->eng = new QRegExpEngine(priv->engineKey); - - if (initMatchState) - priv->matchState.prepareForMatch(priv->eng); + priv->eng = new QRegExpEngine(priv->engineKey); } inline static void prepareEngine(QRegExpPrivate *priv) @@ -3864,6 +3871,7 @@ inline static void prepareEngine(QRegExpPrivate *priv) if (priv->eng) return; prepareEngine_helper(priv); + priv->matchState.prepareForMatch(priv->eng); } static void prepareEngineForMatch(QRegExpPrivate *priv, const QString &str) diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 13eff07c04..af2e46dd00 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -43,7 +43,7 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qhashfunctions.h> -#include <QtCore/qreadwritelock.h> +#include <QtCore/qmutex.h> #include <QtCore/qvector.h> #include <QtCore/qstringlist.h> #include <QtCore/qdebug.h> @@ -720,21 +720,14 @@ QT_BEGIN_NAMESPACE to the \c{/u} modifier in Perl regular expressions. \value OptimizeOnFirstUsageOption - The regular expression will be optimized (and possibly - JIT-compiled) on its first usage, instead of after a certain (undefined) - number of usages. See also \l{QRegularExpression::}{optimize()}. - This enum value has been introduced in Qt 5.4. + This option is ignored. A regular expression is automatically optimized + (including JIT compiling) the first time it is used. This enum value + was introduced in Qt 5.4. \value DontAutomaticallyOptimizeOption - Regular expressions are automatically optimized after a - certain number of usages; setting this option prevents such - optimizations, therefore avoiding possible unpredictable spikes in - CPU and memory usage. If both this option and the - \c{OptimizeOnFirstUsageOption} option are set, then this option takes - precedence. Note: this option will still let the regular expression - to be optimized by manually calling - \l{QRegularExpression::}{optimize()}. This enum value has been - introduced in Qt 5.4. + This option is ignored. A regular expression is automatically optimized + (including JIT compiling) the first time it is used. This enum value + was introduced in Qt 5.4. */ /*! @@ -791,12 +784,81 @@ QT_BEGIN_NAMESPACE Qt 5.4. */ -// after how many usages we optimize the regexp -#ifdef QT_BUILD_INTERNAL -Q_AUTOTEST_EXPORT unsigned int qt_qregularexpression_optimize_after_use_count = 10; -#else -static const unsigned int qt_qregularexpression_optimize_after_use_count = 10; -#endif // QT_BUILD_INTERNAL +namespace QtPrivate { +/*! + internal +*/ +QString wildcardToRegularExpression(const QString &wildcardString) +{ + const int wclen = wildcardString.length(); + QString rx; + int i = 0; + bool hasNegativeBracket = false; + const QChar *wc = wildcardString.unicode(); + + while (i < wclen) { + const QChar c = wc[i++]; + switch (c.unicode()) { + case '*': + rx += QLatin1String(".*"); + break; + case '?': + rx += QLatin1Char('.'); + break; + case '$': + case '(': + case ')': + case '+': + case '.': + case '^': + case '{': + case '|': + case '}': + rx += QLatin1Char('\\'); + rx += c; + break; + case '[': + // Support for the [!abc] or [!a-c] syntax + // Implements a negative look-behind for one char. + if (wc[i] == QLatin1Char(']')) { + rx += c; + rx += wc[i++]; + } else if (wc[i] == QLatin1Char('!')) { + rx += QLatin1String(".(?<"); + rx += wc[i++]; + rx += c; + hasNegativeBracket = true; + } else { + rx += c; + } + + if (i < wclen) { + if (rx[i] == QLatin1Char(']')) + rx += wc[i++]; + while (i < wclen && wc[i] != QLatin1Char(']')) { + if (wc[i] == QLatin1Char('\\')) + rx += QLatin1Char('\\'); + rx += wc[i++]; + } + } + break; + case ']': + rx += c; + // Closes the negative look-behind expression. + if (hasNegativeBracket) { + rx += QLatin1Char(')'); + hasNegativeBracket = false; + } + break; + default: + rx += c; + break; + } + } + + return rx; +} +} /*! \internal @@ -847,13 +909,7 @@ struct QRegularExpressionPrivate : QSharedData void cleanCompiledPattern(); void compilePattern(); void getPatternInfo(); - - enum OptimizePatternOption { - LazyOptimizeOption, - ImmediateOptimizeOption - }; - - void optimizePattern(OptimizePatternOption option); + void optimizePattern(); enum CheckSubjectStringOption { CheckSubjectString, @@ -878,16 +934,15 @@ struct QRegularExpressionPrivate : QSharedData // *All* of the following members are managed while holding this mutex, // except for isDirty which is set to true by QRegularExpression setters // (right after a detach happened). - mutable QReadWriteLock mutex; + mutable QMutex mutex; // The PCRE code pointer is reference-counted by the QRegularExpressionPrivate // objects themselves; when the private is copied (i.e. a detach happened) - // they are set to 0 + // it is set to nullptr pcre2_code_16 *compiledPattern; int errorCode; int errorOffset; int capturingCount; - unsigned int usedCount; bool usingCrLfNewlines; bool isDirty; }; @@ -952,11 +1007,10 @@ QRegularExpressionPrivate::QRegularExpressionPrivate() patternOptions(0), pattern(), mutex(), - compiledPattern(0), + compiledPattern(nullptr), errorCode(0), errorOffset(-1), capturingCount(0), - usedCount(0), usingCrLfNewlines(false), isDirty(true) { @@ -974,8 +1028,8 @@ QRegularExpressionPrivate::~QRegularExpressionPrivate() \internal Copies the private, which means copying only the pattern and the pattern - options. The compiledPattern and the studyData pointers are NOT copied (we - do not own them any more), and in general all the members set when + options. The compiledPattern pointer is NOT copied (we + do not own it any more), and in general all the members set when compiling a pattern are set to default values. isDirty is set back to true so that the pattern has to be recompiled again. */ @@ -984,11 +1038,10 @@ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPri patternOptions(other.patternOptions), pattern(other.pattern), mutex(), - compiledPattern(0), + compiledPattern(nullptr), errorCode(0), errorOffset(-1), capturingCount(0), - usedCount(0), usingCrLfNewlines(false), isDirty(true) { @@ -1000,11 +1053,10 @@ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPri void QRegularExpressionPrivate::cleanCompiledPattern() { pcre2_code_free_16(compiledPattern); - compiledPattern = 0; + compiledPattern = nullptr; errorCode = 0; errorOffset = -1; capturingCount = 0; - usedCount = 0; usingCrLfNewlines = false; } @@ -1013,7 +1065,7 @@ void QRegularExpressionPrivate::cleanCompiledPattern() */ void QRegularExpressionPrivate::compilePattern() { - const QWriteLocker lock(&mutex); + const QMutexLocker lock(&mutex); if (!isDirty) return; @@ -1040,6 +1092,7 @@ void QRegularExpressionPrivate::compilePattern() errorCode = 0; } + optimizePattern(); getPatternInfo(); } @@ -1140,15 +1193,10 @@ static bool isJitEnabled() The purpose of the function is to call pcre2_jit_compile_16, which JIT-compiles the pattern. - It gets called by doMatch() every time a match is performed. - - As of now, the optimizations on the pattern are performed after a certain - number of usages (i.e. the qt_qregularexpression_optimize_after_use_count - constant) unless the DontAutomaticallyOptimizeOption option is set on the - QRegularExpression object, or anyhow by calling optimize() (which will pass - ImmediateOptimizeOption). + It gets called when a pattern is recompiled by us (in compilePattern()), + under mutex protection. */ -void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option) +void QRegularExpressionPrivate::optimizePattern() { Q_ASSERT(compiledPattern); @@ -1157,11 +1205,6 @@ void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option) if (!enableJit) return; - const QWriteLocker lock(&mutex); - - if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count)) - return; - pcre2_jit_compile_16(compiledPattern, PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD); } @@ -1267,22 +1310,12 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString return priv; } - // skip optimizing and doing the actual matching if NoMatch type was requested + // skip doing the actual matching if NoMatch type was requested if (matchType == QRegularExpression::NoMatch) { priv->isValid = true; return priv; } - if (!(patternOptions & QRegularExpression::DontAutomaticallyOptimizeOption)) { - const OptimizePatternOption optimizePatternOption = - (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption) - ? ImmediateOptimizeOption - : LazyOptimizeOption; - - // this is mutex protected - const_cast<QRegularExpressionPrivate *>(this)->optimizePattern(optimizePatternOption); - } - int pcreOptions = convertToPcreOptions(matchOptions); if (matchType == QRegularExpression::PartialPreferCompleteMatch) @@ -1307,8 +1340,6 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString int result; - QReadLocker lock(&mutex); - if (!previousMatchWasEmpty) { result = safe_pcre2_match_16(compiledPattern, subjectUtf16, subjectLength, @@ -1340,8 +1371,6 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString } } - lock.unlock(); - #ifdef QREGULAREXPRESSION_DEBUG qDebug() << "Matching" << pattern << "against" << subject << "starting at" << subjectStart << "len" << subjectLength @@ -1554,6 +1583,47 @@ void QRegularExpression::setPattern(const QString &pattern) } /*! + \since 5.12 + + Sets the pattern string of the regular expression to \a wildcard pattern. + The pattern options are left unchanged. + + \warning Unlike QRegExp, this implementation follows closely the definition + of wildcard for glob patterns: + \table + \row \li \b{c} + \li Any character represents itself apart from those mentioned + below. Thus \b{c} matches the character \e c. + \row \li \b{?} + \li Matches any single character. It is the same as + \b{.} in full regexps. + \row \li \b{*} + \li Matches zero or more of any characters. It is the + same as \b{.*} in full regexps. + \row \li \b{[abc]} + \li Matches one character given in the bracket. + \row \li \b{[a-c]} + \li Matches one character from the range given in the bracket. + \row \li \b{[!abc]} + \li Matches one character that is not given in the bracket. + \row \li \b{[!a-c]} + \li matches one character that is not from the range given in the + bracket. + \endtable + + \note This function generates a regular expression that will act following + the wildcard pattern given. However the content of the regular expression + will not be the same as the one set. + + \sa pattern(), setPattern() +*/ +void QRegularExpression::setWildcardPattern(const QString &pattern) +{ + setPattern(QtPrivate::wildcardToRegularExpression(pattern)); +} + + +/*! Returns the pattern options for the regular expression. \sa setPatternOptions(), pattern() @@ -1810,22 +1880,14 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QStringRef /*! \since 5.4 - Forces an immediate optimization of the pattern, including - JIT-compiling it (if the JIT compiler is enabled). - - Patterns are normally optimized only after a certain number of usages. - If you can predict that this QRegularExpression object is going to be - used for several matches, it may be convenient to optimize it in - advance by calling this function. + Compiles the pattern immediately, including JIT compiling it (if + the JIT is enabled) for optimization. - \sa QRegularExpression::OptimizeOnFirstUsageOption + \sa isValid(), {Debugging Code that Uses QRegularExpression} */ void QRegularExpression::optimize() const { - if (!isValid()) // will compile the pattern - return; - - d->optimizePattern(QRegularExpressionPrivate::ImmediateOptimizeOption); + d.data()->compilePattern(); } /*! diff --git a/src/corelib/tools/qregularexpression.h b/src/corelib/tools/qregularexpression.h index 398fc9ec9c..d0f90b90b3 100644 --- a/src/corelib/tools/qregularexpression.h +++ b/src/corelib/tools/qregularexpression.h @@ -73,8 +73,8 @@ public: InvertedGreedinessOption = 0x0010, DontCaptureOption = 0x0020, UseUnicodePropertiesOption = 0x0040, - OptimizeOnFirstUsageOption = 0x0080, - DontAutomaticallyOptimizeOption = 0x0100 + OptimizeOnFirstUsageOption Q_DECL_ENUMERATOR_DEPRECATED_X("This option does not have any effect since Qt 5.12") = 0x0080, + DontAutomaticallyOptimizeOption Q_DECL_ENUMERATOR_DEPRECATED_X("This option does not have any effect since Qt 5.12") = 0x0100, }; Q_DECLARE_FLAGS(PatternOptions, PatternOption) @@ -96,6 +96,7 @@ public: QString pattern() const; void setPattern(const QString &pattern); + void setWildcardPattern(const QString &pattern); bool isValid() const; int patternErrorOffset() const; diff --git a/src/corelib/tools/qregularexpression_p.h b/src/corelib/tools/qregularexpression_p.h new file mode 100644 index 0000000000..f5455de853 --- /dev/null +++ b/src/corelib/tools/qregularexpression_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Samuel Gaist <samuel.gaist@edeltech.ch> +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $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 +** 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 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$ +** +****************************************************************************/ + +#ifndef QREGULAREXPRESSION_P_H +#define QREGULAREXPRESSION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qglobal_p.h> + +#include <qregularexpression.h> +#include <qstring.h> + +QT_REQUIRE_CONFIG(regularexpression); + +QT_BEGIN_NAMESPACE + +namespace QtPrivate { +QString wildcardToRegularExpression(const QString &expression); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/tools/qringbuffer.cpp b/src/corelib/tools/qringbuffer.cpp index eb7bdfe95c..59650ed2f7 100644 --- a/src/corelib/tools/qringbuffer.cpp +++ b/src/corelib/tools/qringbuffer.cpp @@ -312,12 +312,14 @@ qint64 QRingBuffer::peek(char *data, qint64 maxLength, qint64 pos) const Q_ASSERT(maxLength >= 0 && pos >= 0); qint64 readSoFar = 0; - for (int i = 0; readSoFar < maxLength && i < buffers.size(); ++i) { - qint64 blockLength = buffers[i].size(); + for (const QRingChunk &chunk : buffers) { + if (readSoFar == maxLength) + break; + qint64 blockLength = chunk.size(); if (pos < blockLength) { blockLength = qMin(blockLength - pos, maxLength - readSoFar); - memcpy(data + readSoFar, buffers[i].data() + pos, blockLength); + memcpy(data + readSoFar, chunk.data() + pos, blockLength); readSoFar += blockLength; pos = 0; } else { diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 7ded120ab7..6640c8486d 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -131,6 +131,7 @@ public: inline iterator operator--(int) { iterator r = *this; --i; return r; } inline iterator operator+(int j) const { return i + j; } inline iterator operator-(int j) const { return i - j; } + friend inline iterator operator+(int j, iterator k) { return k + j; } inline iterator &operator+=(int j) { i += j; return *this; } inline iterator &operator-=(int j) { i -= j; return *this; } }; @@ -165,6 +166,7 @@ public: inline const_iterator operator--(int) { const_iterator r = *this; --i; return r; } inline const_iterator operator+(int j) const { return i + j; } inline const_iterator operator-(int j) const { return i - j; } + friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } inline const_iterator &operator+=(int j) { i += j; return *this; } inline const_iterator &operator-=(int j) { i -= j; return *this; } }; diff --git a/src/corelib/tools/qshareddata.h b/src/corelib/tools/qshareddata.h index 780f2331a8..3ef134a99d 100644 --- a/src/corelib/tools/qshareddata.h +++ b/src/corelib/tools/qshareddata.h @@ -136,6 +136,18 @@ private: T *d; }; +template <class T> inline bool operator==(std::nullptr_t p1, const QSharedDataPointer<T> &p2) +{ + Q_UNUSED(p1); + return !p2; +} + +template <class T> inline bool operator==(const QSharedDataPointer<T> &p1, std::nullptr_t p2) +{ + Q_UNUSED(p2); + return !p1; +} + template <class T> class QExplicitlySharedDataPointer { public: @@ -271,6 +283,18 @@ Q_INLINE_TEMPLATE QExplicitlySharedDataPointer<T>::QExplicitlySharedDataPointer( : d(adata) { if (d) d->ref.ref(); } +template <class T> inline bool operator==(std::nullptr_t p1, const QExplicitlySharedDataPointer<T> &p2) +{ + Q_UNUSED(p1); + return !p2; +} + +template <class T> inline bool operator==(const QExplicitlySharedDataPointer<T> &p1, std::nullptr_t p2) +{ + Q_UNUSED(p2); + return !p1; +} + template <class T> Q_INLINE_TEMPLATE void qSwap(QSharedDataPointer<T> &p1, QSharedDataPointer<T> &p2) { p1.swap(p2); } diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index 25340f2d02..85efd3cded 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -80,6 +80,43 @@ QT_BEGIN_NAMESPACE +/* + * Use kdesdk/scripts/generate_string_table.pl to update the table below. Note + * we remove the terminating -1 that the script adds. + */ + +// begin generated +#if defined(Q_PROCESSOR_ARM) +/* Data: + neon + crc32 + */ +static const char features_string[] = + " neon\0" + " crc32\0" + "\0"; +static const int features_indices[] = { 0, 6 }; +#elif defined(Q_PROCESSOR_MIPS) +/* Data: + dsp + dspr2 +*/ +static const char features_string[] = + " dsp\0" + " dspr2\0" + "\0"; + +static const int features_indices[] = { + 0, 5 +}; +#elif defined(Q_PROCESSOR_X86) +# include "qsimd_x86.cpp" // generated by util/x86simdgen +#else +static const char features_string[] = ""; +static const int features_indices[] = { }; +#endif +// end generated + #if defined (Q_OS_NACL) static inline uint detectProcessorFeatures() { @@ -222,29 +259,32 @@ static void cpuidFeatures01(uint &ecx, uint &edx) inline void __cpuidex(int info[4], int, __int64) { memset(info, 0, 4*sizeof(int));} #endif -static void cpuidFeatures07_00(uint &ebx, uint &ecx) +static void cpuidFeatures07_00(uint &ebx, uint &ecx, uint &edx) { #if defined(Q_CC_GNU) qregisteruint rbx; // in case it's 64-bit qregisteruint rcx = 0; + qregisteruint rdx = 0; asm ("xchg " PICreg", %0\n" "cpuid\n" "xchg " PICreg", %0\n" - : "=&r" (rbx), "+&c" (rcx) - : "a" (7) - : "%edx"); + : "=&r" (rbx), "+&c" (rcx), "+&d" (rdx) + : "a" (7)); ebx = rbx; ecx = rcx; + edx = rdx; #elif defined(Q_OS_WIN) int info[4]; __cpuidex(info, 7, 0); ebx = info[1]; ecx = info[2]; + edx = info[3]; #elif defined(Q_CC_GHS) unsigned int info[4]; __CPUIDEX(7, 0, info); ebx = info[1]; ecx = info[2]; + edx = info[3]; #endif } @@ -282,13 +322,8 @@ static quint64 detectProcessorFeatures() AVXState = XMM0_15 | YMM0_15Hi128, AVX512State = AVXState | OpMask | ZMM0_15Hi256 | ZMM16_31 }; - static const quint64 AllAVX512 = (Q_UINT64_C(1) << CpuFeatureAVX512F) | (Q_UINT64_C(1) << CpuFeatureAVX512CD) | - (Q_UINT64_C(1) << CpuFeatureAVX512ER) | (Q_UINT64_C(1) << CpuFeatureAVX512PF) | - (Q_UINT64_C(1) << CpuFeatureAVX512BW) | (Q_UINT64_C(1) << CpuFeatureAVX512DQ) | - (Q_UINT64_C(1) << CpuFeatureAVX512VL) | - (Q_UINT64_C(1) << CpuFeatureAVX512IFMA) | (Q_UINT64_C(1) << CpuFeatureAVX512VBMI); - static const quint64 AllAVX2 = (Q_UINT64_C(1) << CpuFeatureAVX2) | AllAVX512; - static const quint64 AllAVX = (Q_UINT64_C(1) << CpuFeatureAVX) | AllAVX2; + static const quint64 AllAVX2 = CpuFeatureAVX2 | AllAVX512; + static const quint64 AllAVX = CpuFeatureAVX | AllAVX2; quint64 features = 0; int cpuidLevel = maxBasicCpuidSupported(); @@ -299,52 +334,33 @@ static quint64 detectProcessorFeatures() Q_ASSERT(cpuidLevel >= 1); #endif - uint cpuid01ECX = 0, cpuid01EDX = 0; - cpuidFeatures01(cpuid01ECX, cpuid01EDX); - - // the low 32-bits of features is cpuid01ECX - // note: we need to check OS support for saving the AVX register state - features = cpuid01ECX; - -#if defined(Q_PROCESSOR_X86_32) - // x86 might not have SSE2 support - if (cpuid01EDX & (1u << 26)) - features |= Q_UINT64_C(1) << CpuFeatureSSE2; - else - features &= ~(Q_UINT64_C(1) << CpuFeatureSSE2); - // we should verify that the OS enabled saving of the SSE state... -#else - // x86-64 or x32 - features |= Q_UINT64_C(1) << CpuFeatureSSE2; -#endif + uint results[X86CpuidMaxLeaf] = {}; + cpuidFeatures01(results[Leaf1ECX], results[Leaf1EDX]); + if (cpuidLevel >= 7) + cpuidFeatures07_00(results[Leaf7_0EBX], results[Leaf7_0ECX], results[Leaf7_0EDX]); + + // populate our feature list + for (uint i = 0; i < sizeof(x86_locators) / sizeof(x86_locators[0]); ++i) { + uint word = x86_locators[i] / 32; + uint bit = 1U << (x86_locators[i] % 32); + quint64 feature = Q_UINT64_C(1) << (i + 1); + if (results[word] & bit) + features |= feature; + } + // now check the AVX state uint xgetbvA = 0, xgetbvD = 0; - if (cpuid01ECX & (1u << 27)) { + if (results[Leaf1ECX] & (1u << 27)) { // XGETBV enabled xgetbv(0, xgetbvA, xgetbvD); } - uint cpuid0700EBX = 0; - uint cpuid0700ECX = 0; - if (cpuidLevel >= 7) { - cpuidFeatures07_00(cpuid0700EBX, cpuid0700ECX); - - // the high 32-bits of features is cpuid0700EBX - features |= quint64(cpuid0700EBX) << 32; - } - if ((xgetbvA & AVXState) != AVXState) { // support for YMM registers is disabled, disable all AVX features &= ~AllAVX; } else if ((xgetbvA & AVX512State) != AVX512State) { // support for ZMM registers or mask registers is disabled, disable all AVX512 features &= ~AllAVX512; - } else { - // this feature is out of order - if (cpuid0700ECX & (1u << 1)) - features |= Q_UINT64_C(1) << CpuFeatureAVX512VBMI; - else - features &= ~(Q_UINT64_C(1) << CpuFeatureAVX512VBMI); } return features; @@ -493,152 +509,6 @@ static inline uint detectProcessorFeatures() } #endif -/* - * Use kdesdk/scripts/generate_string_table.pl to update the table below. Note - * that the x86 version has a lot of blanks that must be kept and that the - * offset table's type is changed to make the table smaller. We also remove the - * terminating -1 that the script adds. - */ - -// begin generated -#if defined(Q_PROCESSOR_ARM) -/* Data: - neon - crc32 - */ -static const char features_string[] = - " neon\0" - " crc32\0" - "\0"; -static const int features_indices[] = { 0, 6 }; -#elif defined(Q_PROCESSOR_MIPS) -/* Data: - dsp - dspr2 -*/ -static const char features_string[] = - " dsp\0" - " dspr2\0" - "\0"; - -static const int features_indices[] = { - 0, 5 -}; -#elif defined(Q_PROCESSOR_X86) -/* Data: - sse3 - sse2 - avx512vbmi - - - - - - - ssse3 - - - fma - cmpxchg16b - - - - - - sse4.1 - sse4.2 - - movbe - popcnt - - aes - - - avx - f16c - rdrand - - - - - bmi - hle - avx2 - - - bmi2 - - - rtm - - - - - avx512f - avx512dq - rdseed - - - avx512ifma - - - - - avx512pf - avx512er - avx512cd - sha - avx512bw - avx512vl - */ -static const char features_string[] = - " sse3\0" - " sse2\0" - " avx512vbmi\0" - " ssse3\0" - " fma\0" - " cmpxchg16b\0" - " sse4.1\0" - " sse4.2\0" - " movbe\0" - " popcnt\0" - " aes\0" - " avx\0" - " f16c\0" - " rdrand\0" - " bmi\0" - " hle\0" - " avx2\0" - " bmi2\0" - " rtm\0" - " avx512f\0" - " avx512dq\0" - " rdseed\0" - " avx512ifma\0" - " avx512pf\0" - " avx512er\0" - " avx512cd\0" - " sha\0" - " avx512bw\0" - " avx512vl\0" - "\0"; - -static const quint8 features_indices[] = { - 0, 6, 12, 5, 5, 5, 5, 5, - 5, 24, 5, 5, 31, 36, 5, 5, - 5, 5, 5, 48, 56, 5, 64, 71, - 5, 79, 5, 5, 84, 89, 95, 5, - 5, 5, 5, 103, 108, 113, 5, 5, - 119, 5, 5, 125, 5, 5, 5, 5, - 130, 139, 149, 5, 5, 157, 5, 5, - 5, 5, 169, 179, 189, 199, 204, 214 -}; -#else -static const char features_string[] = ""; -static const int features_indices[] = { }; -#endif -// end generated - static const int features_count = (sizeof features_indices) / (sizeof features_indices[0]); // record what CPU features were enabled by default in this Qt build diff --git a/src/corelib/tools/qsimd_p.h b/src/corelib/tools/qsimd_p.h index 18684caefb..af262ec88f 100644 --- a/src/corelib/tools/qsimd_p.h +++ b/src/corelib/tools/qsimd_p.h @@ -232,32 +232,7 @@ # define __RDRND__ 1 # endif -#define QT_FUNCTION_TARGET_STRING_SSE2 "sse2" -#define QT_FUNCTION_TARGET_STRING_SSE3 "sse3" -#define QT_FUNCTION_TARGET_STRING_SSSE3 "ssse3" -#define QT_FUNCTION_TARGET_STRING_SSE4_1 "sse4.1" -#define QT_FUNCTION_TARGET_STRING_SSE4_2 "sse4.2" -#define QT_FUNCTION_TARGET_STRING_AVX "avx" -#define QT_FUNCTION_TARGET_STRING_AVX2 "avx2" -#define QT_FUNCTION_TARGET_STRING_AVX512F "avx512f" -#define QT_FUNCTION_TARGET_STRING_AVX512CD "avx512cd" -#define QT_FUNCTION_TARGET_STRING_AVX512ER "avx512er" -#define QT_FUNCTION_TARGET_STRING_AVX512PF "avx512pf" -#define QT_FUNCTION_TARGET_STRING_AVX512BW "avx512bw" -#define QT_FUNCTION_TARGET_STRING_AVX512DQ "avx512dq" -#define QT_FUNCTION_TARGET_STRING_AVX512VL "avx512vl" -#define QT_FUNCTION_TARGET_STRING_AVX512IFMA "avx512ifma" -#define QT_FUNCTION_TARGET_STRING_AVX512VBMI "avx512vbmi" - -#define QT_FUNCTION_TARGET_STRING_AES "aes,sse4.2" -#define QT_FUNCTION_TARGET_STRING_PCLMUL "pclmul,sse4.2" -#define QT_FUNCTION_TARGET_STRING_POPCNT "popcnt" -#define QT_FUNCTION_TARGET_STRING_F16C "f16c,avx" -#define QT_FUNCTION_TARGET_STRING_RDRND "rdrnd" -#define QT_FUNCTION_TARGET_STRING_BMI "bmi" -#define QT_FUNCTION_TARGET_STRING_BMI2 "bmi2" -#define QT_FUNCTION_TARGET_STRING_RDSEED "rdseed" -#define QT_FUNCTION_TARGET_STRING_SHA "sha" +# include "qsimd_x86_p.h" #endif /* Q_PROCESSOR_X86 */ @@ -292,148 +267,36 @@ QT_BEGIN_NAMESPACE +#ifndef Q_PROCESSOR_X86 enum CPUFeatures { #if defined(Q_PROCESSOR_ARM) - CpuFeatureNEON = 0, + CpuFeatureNEON = 2, CpuFeatureARM_NEON = CpuFeatureNEON, - CpuFeatureCRC32 = 1, + CpuFeatureCRC32 = 4, #elif defined(Q_PROCESSOR_MIPS) - CpuFeatureDSP = 0, - CpuFeatureDSPR2 = 1, -#elif defined(Q_PROCESSOR_X86) - // The order of the flags is jumbled so it matches most closely the bits in CPUID - // Out of order: - CpuFeatureSSE2 = 1, // uses the bit for PCLMULQDQ - // in level 1, ECX - CpuFeatureSSE3 = (0 + 0), - CpuFeatureSSSE3 = (0 + 9), - CpuFeatureSSE4_1 = (0 + 19), - CpuFeatureSSE4_2 = (0 + 20), - CpuFeatureMOVBE = (0 + 22), - CpuFeaturePOPCNT = (0 + 23), - CpuFeatureAES = (0 + 25), - CpuFeatureAVX = (0 + 28), - CpuFeatureF16C = (0 + 29), - CpuFeatureRDRND = (0 + 30), - // 31 is always zero and we've used it for the QSimdInitialized - - // in level 7, leaf 0, EBX - CpuFeatureBMI = (32 + 3), - CpuFeatureHLE = (32 + 4), - CpuFeatureAVX2 = (32 + 5), - CpuFeatureBMI2 = (32 + 8), - CpuFeatureRTM = (32 + 11), - CpuFeatureAVX512F = (32 + 16), - CpuFeatureAVX512DQ = (32 + 17), - CpuFeatureRDSEED = (32 + 18), - CpuFeatureAVX512IFMA = (32 + 21), - CpuFeatureAVX512PF = (32 + 26), - CpuFeatureAVX512ER = (32 + 27), - CpuFeatureAVX512CD = (32 + 28), - CpuFeatureSHA = (32 + 29), - CpuFeatureAVX512BW = (32 + 30), - CpuFeatureAVX512VL = (32 + 31), - - // in level 7, leaf 0, ECX (out of order, for now) - CpuFeatureAVX512VBMI = 2, // uses the bit for DTES64 + CpuFeatureDSP = 2, + CpuFeatureDSPR2 = 4, #endif // used only to indicate that the CPU detection was initialised - QSimdInitialized = 0x80000000 + QSimdInitialized = 1 }; static const quint64 qCompilerCpuFeatures = 0 -#if defined __SHA__ - | (Q_UINT64_C(1) << CpuFeatureSHA) -#endif -#if defined __AES__ - | (Q_UINT64_C(1) << CpuFeatureAES) -#endif -#if defined __RTM__ - | (Q_UINT64_C(1) << CpuFeatureRTM) -#endif -#ifdef __RDRND__ - | (Q_UINT64_C(1) << CpuFeatureRDRND) -#endif -#ifdef __RDSEED__ - | (Q_UINT64_C(1) << CpuFeatureRDSEED) -#endif -#if defined __BMI__ - | (Q_UINT64_C(1) << CpuFeatureBMI) -#endif -#if defined __BMI2__ - | (Q_UINT64_C(1) << CpuFeatureBMI2) -#endif -#if defined __F16C__ - | (Q_UINT64_C(1) << CpuFeatureF16C) -#endif -#if defined __POPCNT__ - | (Q_UINT64_C(1) << CpuFeaturePOPCNT) -#endif -#if defined __MOVBE__ // GCC and Clang don't seem to define this - | (Q_UINT64_C(1) << CpuFeatureMOVBE) -#endif -#if defined __AVX512F__ - | (Q_UINT64_C(1) << CpuFeatureAVX512F) -#endif -#if defined __AVX512CD__ - | (Q_UINT64_C(1) << CpuFeatureAVX512CD) -#endif -#if defined __AVX512ER__ - | (Q_UINT64_C(1) << CpuFeatureAVX512ER) -#endif -#if defined __AVX512PF__ - | (Q_UINT64_C(1) << CpuFeatureAVX512PF) -#endif -#if defined __AVX512BW__ - | (Q_UINT64_C(1) << CpuFeatureAVX512BW) -#endif -#if defined __AVX512DQ__ - | (Q_UINT64_C(1) << CpuFeatureAVX512DQ) -#endif -#if defined __AVX512VL__ - | (Q_UINT64_C(1) << CpuFeatureAVX512VL) -#endif -#if defined __AVX512IFMA__ - | (Q_UINT64_C(1) << CpuFeatureAVX512IFMA) -#endif -#if defined __AVX512VBMI__ - | (Q_UINT64_C(1) << CpuFeatureAVX512VBMI) -#endif -#if defined __AVX2__ - | (Q_UINT64_C(1) << CpuFeatureAVX2) -#endif -#if defined __AVX__ - | (Q_UINT64_C(1) << CpuFeatureAVX) -#endif -#if defined __SSE4_2__ - | (Q_UINT64_C(1) << CpuFeatureSSE4_2) -#endif -#if defined __SSE4_1__ - | (Q_UINT64_C(1) << CpuFeatureSSE4_1) -#endif -#if defined __SSSE3__ - | (Q_UINT64_C(1) << CpuFeatureSSSE3) -#endif -#if defined __SSE3__ - | (Q_UINT64_C(1) << CpuFeatureSSE3) -#endif -#if defined __SSE2__ - | (Q_UINT64_C(1) << CpuFeatureSSE2) -#endif #if defined __ARM_NEON__ - | (Q_UINT64_C(1) << CpuFeatureNEON) + | CpuFeatureNEON #endif #if defined __ARM_FEATURE_CRC32 - | (Q_UINT64_C(1) << CpuFeatureCRC32) + | CpuFeatureCRC32 #endif #if defined __mips_dsp - | (Q_UINT64_C(1) << CpuFeatureDSP) + | CpuFeatureDSP #endif #if defined __mips_dspr2 - | (Q_UINT64_C(1) << CpuFeatureDSPR2) + | CpuFeatureDSPR2 #endif ; +#endif #ifdef Q_ATOMIC_INT64_IS_SUPPORTED extern Q_CORE_EXPORT QBasicAtomicInteger<quint64> qt_cpu_features[1]; @@ -459,8 +322,8 @@ static inline quint64 qCpuFeatures() return features; } -#define qCpuHasFeature(feature) ((qCompilerCpuFeatures & (Q_UINT64_C(1) << CpuFeature ## feature)) \ - || (qCpuFeatures() & (Q_UINT64_C(1) << CpuFeature ## feature))) +#define qCpuHasFeature(feature) (((qCompilerCpuFeatures & CpuFeature ## feature) == CpuFeature ## feature) \ + || ((qCpuFeatures() & CpuFeature ## feature) == CpuFeature ## feature)) #define ALIGNMENT_PROLOGUE_16BYTES(ptr, i, length) \ for (; i < static_cast<int>(qMin(static_cast<quintptr>(length), ((4 - ((reinterpret_cast<quintptr>(ptr) >> 2) & 0x3)) & 0x3))); ++i) diff --git a/src/corelib/tools/qsimd_x86.cpp b/src/corelib/tools/qsimd_x86.cpp new file mode 100644 index 0000000000..509af464b2 --- /dev/null +++ b/src/corelib/tools/qsimd_x86.cpp @@ -0,0 +1,116 @@ +// This is a generated file. DO NOT EDIT. +// Please see util/x86simdgen/generate.pl +#include "qsimd_p.h" + +static const char features_string[] = + " sse2\0" + " sse3\0" + " ssse3\0" + " fma\0" + " sse4.1\0" + " sse4.2\0" + " movbe\0" + " popcnt\0" + " aes\0" + " avx\0" + " f16c\0" + " rdrnd\0" + " bmi\0" + " hle\0" + " avx2\0" + " bmi2\0" + " rtm\0" + " avx512f\0" + " avx512dq\0" + " rdseed\0" + " avx512ifma\0" + " avx512pf\0" + " avx512er\0" + " avx512cd\0" + " sha\0" + " avx512bw\0" + " avx512vl\0" + " avx512vbmi\0" + " avx512vbmi2\0" + " gfni\0" + " vaes\0" + " avx512vnni\0" + " avx512bitalg\0" + " avx512vpopcntdq\0" + " avx5124nniw\0" + " avx5124fmaps\0" + "\0"; + +static const quint16 features_indices[] = { + 306, 0, 6, 12, 19, 24, 32, 40, + 47, 55, 60, 65, 71, 78, 83, 88, + 94, 100, 105, 114, 124, 132, 144, 154, + 164, 174, 179, 189, 199, 211, 224, 230, + 236, 248, 262, 279, 292 +}; + +enum X86CpuidLeaves { + Leaf1ECX, + Leaf1EDX, + Leaf7_0EBX, + Leaf7_0ECX, + Leaf7_0EDX, + X86CpuidMaxLeaf +}; + +static const quint8 x86_locators[] = { + Leaf1EDX*32 + 26, // sse2 + Leaf1ECX*32 + 0, // sse3 + Leaf1ECX*32 + 9, // ssse3 + Leaf1ECX*32 + 12, // fma + Leaf1ECX*32 + 19, // sse4.1 + Leaf1ECX*32 + 20, // sse4.2 + Leaf1ECX*32 + 22, // movbe + Leaf1ECX*32 + 23, // popcnt + Leaf1ECX*32 + 25, // aes + Leaf1ECX*32 + 28, // avx + Leaf1ECX*32 + 29, // f16c + Leaf1ECX*32 + 30, // rdrnd + Leaf7_0EBX*32 + 3, // bmi + Leaf7_0EBX*32 + 4, // hle + Leaf7_0EBX*32 + 5, // avx2 + Leaf7_0EBX*32 + 8, // bmi2 + Leaf7_0EBX*32 + 11, // rtm + Leaf7_0EBX*32 + 16, // avx512f + Leaf7_0EBX*32 + 17, // avx512dq + Leaf7_0EBX*32 + 18, // rdseed + Leaf7_0EBX*32 + 21, // avx512ifma + Leaf7_0EBX*32 + 26, // avx512pf + Leaf7_0EBX*32 + 27, // avx512er + Leaf7_0EBX*32 + 28, // avx512cd + Leaf7_0EBX*32 + 29, // sha + Leaf7_0EBX*32 + 30, // avx512bw + Leaf7_0EBX*32 + 31, // avx512vl + Leaf7_0ECX*32 + 1, // avx512vbmi + Leaf7_0ECX*32 + 6, // avx512vbmi2 + Leaf7_0ECX*32 + 8, // gfni + Leaf7_0ECX*32 + 9, // vaes + Leaf7_0ECX*32 + 11, // avx512vnni + Leaf7_0ECX*32 + 12, // avx512bitalg + Leaf7_0ECX*32 + 14, // avx512vpopcntdq + Leaf7_0EDX*32 + 2, // avx5124nniw + Leaf7_0EDX*32 + 3 // avx5124fmaps +}; + +// List of AVX512 features (see detectProcessorFeatures()) +static const quint64 AllAVX512 = 0 + | CpuFeatureAVX512F + | CpuFeatureAVX512DQ + | CpuFeatureAVX512IFMA + | CpuFeatureAVX512PF + | CpuFeatureAVX512ER + | CpuFeatureAVX512CD + | CpuFeatureAVX512BW + | CpuFeatureAVX512VL + | CpuFeatureAVX512VBMI + | CpuFeatureAVX512VBMI2 + | CpuFeatureAVX512VNNI + | CpuFeatureAVX512BITALG + | CpuFeatureAVX512VPOPCNTDQ + | CpuFeatureAVX5124NNIW + | CpuFeatureAVX5124FMAPS; diff --git a/src/corelib/tools/qsimd_x86_p.h b/src/corelib/tools/qsimd_x86_p.h new file mode 100644 index 0000000000..c165ee0b56 --- /dev/null +++ b/src/corelib/tools/qsimd_x86_p.h @@ -0,0 +1,224 @@ +// This is a generated file. DO NOT EDIT. +// Please see util/x86simdgen/generate.pl +#ifndef QSIMD_P_H +# error "Please include <private/qsimd_p.h> instead" +#endif +#ifndef QSIMD_X86_P_H +#define QSIMD_X86_P_H + +#include "qsimd_p.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +// Macros for QT_FUNCTION_TARGET (for Clang and GCC) +#define QT_FUNCTION_TARGET_STRING_SSE2 "sse2" +#define QT_FUNCTION_TARGET_STRING_SSE3 "sse3" +#define QT_FUNCTION_TARGET_STRING_SSSE3 "ssse3" +#define QT_FUNCTION_TARGET_STRING_FMA "fma" +#define QT_FUNCTION_TARGET_STRING_SSE4_1 "sse4.1" +#define QT_FUNCTION_TARGET_STRING_SSE4_2 "sse4.2" +#define QT_FUNCTION_TARGET_STRING_MOVBE "movbe" +#define QT_FUNCTION_TARGET_STRING_POPCNT "popcnt" +#define QT_FUNCTION_TARGET_STRING_AES "aes,sse4.2" +#define QT_FUNCTION_TARGET_STRING_AVX "avx" +#define QT_FUNCTION_TARGET_STRING_F16C "f16c" +#define QT_FUNCTION_TARGET_STRING_RDRND "rdrnd" +#define QT_FUNCTION_TARGET_STRING_BMI "bmi" +#define QT_FUNCTION_TARGET_STRING_HLE "hle" +#define QT_FUNCTION_TARGET_STRING_AVX2 "avx2" +#define QT_FUNCTION_TARGET_STRING_BMI2 "bmi2" +#define QT_FUNCTION_TARGET_STRING_RTM "rtm" +#define QT_FUNCTION_TARGET_STRING_AVX512F "avx512f" +#define QT_FUNCTION_TARGET_STRING_AVX512DQ "avx512dq" +#define QT_FUNCTION_TARGET_STRING_RDSEED "rdseed" +#define QT_FUNCTION_TARGET_STRING_AVX512IFMA "avx512ifma" +#define QT_FUNCTION_TARGET_STRING_AVX512PF "avx512pf" +#define QT_FUNCTION_TARGET_STRING_AVX512ER "avx512er" +#define QT_FUNCTION_TARGET_STRING_AVX512CD "avx512cd" +#define QT_FUNCTION_TARGET_STRING_SHA "sha" +#define QT_FUNCTION_TARGET_STRING_AVX512BW "avx512bw" +#define QT_FUNCTION_TARGET_STRING_AVX512VL "avx512vl" +#define QT_FUNCTION_TARGET_STRING_AVX512VBMI "avx512vbmi" +#define QT_FUNCTION_TARGET_STRING_AVX512VBMI2 "avx512vbmi2" +#define QT_FUNCTION_TARGET_STRING_GFNI "gfni" +#define QT_FUNCTION_TARGET_STRING_VAES "vaes" +#define QT_FUNCTION_TARGET_STRING_AVX512VNNI "avx512vnni" +#define QT_FUNCTION_TARGET_STRING_AVX512BITALG "avx512bitalg" +#define QT_FUNCTION_TARGET_STRING_AVX512VPOPCNTDQ "avx512vpopcntdq" +#define QT_FUNCTION_TARGET_STRING_AVX5124NNIW "avx5124nniw" +#define QT_FUNCTION_TARGET_STRING_AVX5124FMAPS "avx5124fmaps" + +// used only to indicate that the CPU detection was initialized +static const quint64 QSimdInitialized = Q_UINT64_C(1) << 0; + +// in CPUID Leaf 1, EDX: +static const quint64 CpuFeatureSSE2 = Q_UINT64_C(1) << 1; + +// in CPUID Leaf 1, ECX: +static const quint64 CpuFeatureSSE3 = Q_UINT64_C(1) << 2; +static const quint64 CpuFeatureSSSE3 = Q_UINT64_C(1) << 3; +static const quint64 CpuFeatureFMA = Q_UINT64_C(1) << 4; +static const quint64 CpuFeatureSSE4_1 = Q_UINT64_C(1) << 5; +static const quint64 CpuFeatureSSE4_2 = Q_UINT64_C(1) << 6; +static const quint64 CpuFeatureMOVBE = Q_UINT64_C(1) << 7; +static const quint64 CpuFeaturePOPCNT = Q_UINT64_C(1) << 8; +static const quint64 CpuFeatureAES = Q_UINT64_C(1) << 9; +static const quint64 CpuFeatureAVX = Q_UINT64_C(1) << 10; +static const quint64 CpuFeatureF16C = Q_UINT64_C(1) << 11; +static const quint64 CpuFeatureRDRND = Q_UINT64_C(1) << 12; + +// in CPUID Leaf 7, Sub-leaf 0, EBX: +static const quint64 CpuFeatureBMI = Q_UINT64_C(1) << 13; +static const quint64 CpuFeatureHLE = Q_UINT64_C(1) << 14; +static const quint64 CpuFeatureAVX2 = Q_UINT64_C(1) << 15; +static const quint64 CpuFeatureBMI2 = Q_UINT64_C(1) << 16; +static const quint64 CpuFeatureRTM = Q_UINT64_C(1) << 17; +static const quint64 CpuFeatureAVX512F = Q_UINT64_C(1) << 18; +static const quint64 CpuFeatureAVX512DQ = Q_UINT64_C(1) << 19; +static const quint64 CpuFeatureRDSEED = Q_UINT64_C(1) << 20; +static const quint64 CpuFeatureAVX512IFMA = Q_UINT64_C(1) << 21; +static const quint64 CpuFeatureAVX512PF = Q_UINT64_C(1) << 22; +static const quint64 CpuFeatureAVX512ER = Q_UINT64_C(1) << 23; +static const quint64 CpuFeatureAVX512CD = Q_UINT64_C(1) << 24; +static const quint64 CpuFeatureSHA = Q_UINT64_C(1) << 25; +static const quint64 CpuFeatureAVX512BW = Q_UINT64_C(1) << 26; +static const quint64 CpuFeatureAVX512VL = Q_UINT64_C(1) << 27; + +// in CPUID Leaf 7, Sub-leaf 0, ECX: +static const quint64 CpuFeatureAVX512VBMI = Q_UINT64_C(1) << 28; +static const quint64 CpuFeatureAVX512VBMI2 = Q_UINT64_C(1) << 29; +static const quint64 CpuFeatureGFNI = Q_UINT64_C(1) << 30; +static const quint64 CpuFeatureVAES = Q_UINT64_C(1) << 31; +static const quint64 CpuFeatureAVX512VNNI = Q_UINT64_C(1) << 32; +static const quint64 CpuFeatureAVX512BITALG = Q_UINT64_C(1) << 33; +static const quint64 CpuFeatureAVX512VPOPCNTDQ = Q_UINT64_C(1) << 34; + +// in CPUID Leaf 7, Sub-leaf 0, EDX: +static const quint64 CpuFeatureAVX5124NNIW = Q_UINT64_C(1) << 35; +static const quint64 CpuFeatureAVX5124FMAPS = Q_UINT64_C(1) << 36; + +static const quint64 qCompilerCpuFeatures = 0 +#ifdef __SSE2__ + | (Q_UINT64_C(1) << 1) // CpuFeatureSSE2 +#endif +#ifdef __SSE3__ + | (Q_UINT64_C(1) << 2) // CpuFeatureSSE3 +#endif +#ifdef __SSSE3__ + | (Q_UINT64_C(1) << 3) // CpuFeatureSSSE3 +#endif +#ifdef __FMA__ + | (Q_UINT64_C(1) << 4) // CpuFeatureFMA +#endif +#ifdef __SSE4_1__ + | (Q_UINT64_C(1) << 5) // CpuFeatureSSE4_1 +#endif +#ifdef __SSE4_2__ + | (Q_UINT64_C(1) << 6) // CpuFeatureSSE4_2 +#endif +#ifdef __MOVBE__ + | (Q_UINT64_C(1) << 7) // CpuFeatureMOVBE +#endif +#ifdef __POPCNT__ + | (Q_UINT64_C(1) << 8) // CpuFeaturePOPCNT +#endif +#ifdef __AES__ + | (Q_UINT64_C(1) << 9) // CpuFeatureAES +#endif +#ifdef __AVX__ + | (Q_UINT64_C(1) << 10) // CpuFeatureAVX +#endif +#ifdef __F16C__ + | (Q_UINT64_C(1) << 11) // CpuFeatureF16C +#endif +#ifdef __RDRND__ + | (Q_UINT64_C(1) << 12) // CpuFeatureRDRND +#endif +#ifdef __BMI__ + | (Q_UINT64_C(1) << 13) // CpuFeatureBMI +#endif +#ifdef __HLE__ + | (Q_UINT64_C(1) << 14) // CpuFeatureHLE +#endif +#ifdef __AVX2__ + | (Q_UINT64_C(1) << 15) // CpuFeatureAVX2 +#endif +#ifdef __BMI2__ + | (Q_UINT64_C(1) << 16) // CpuFeatureBMI2 +#endif +#ifdef __RTM__ + | (Q_UINT64_C(1) << 17) // CpuFeatureRTM +#endif +#ifdef __AVX512F__ + | (Q_UINT64_C(1) << 18) // CpuFeatureAVX512F +#endif +#ifdef __AVX512DQ__ + | (Q_UINT64_C(1) << 19) // CpuFeatureAVX512DQ +#endif +#ifdef __RDSEED__ + | (Q_UINT64_C(1) << 20) // CpuFeatureRDSEED +#endif +#ifdef __AVX512IFMA__ + | (Q_UINT64_C(1) << 21) // CpuFeatureAVX512IFMA +#endif +#ifdef __AVX512PF__ + | (Q_UINT64_C(1) << 22) // CpuFeatureAVX512PF +#endif +#ifdef __AVX512ER__ + | (Q_UINT64_C(1) << 23) // CpuFeatureAVX512ER +#endif +#ifdef __AVX512CD__ + | (Q_UINT64_C(1) << 24) // CpuFeatureAVX512CD +#endif +#ifdef __SHA__ + | (Q_UINT64_C(1) << 25) // CpuFeatureSHA +#endif +#ifdef __AVX512BW__ + | (Q_UINT64_C(1) << 26) // CpuFeatureAVX512BW +#endif +#ifdef __AVX512VL__ + | (Q_UINT64_C(1) << 27) // CpuFeatureAVX512VL +#endif +#ifdef __AVX512VBMI__ + | (Q_UINT64_C(1) << 28) // CpuFeatureAVX512VBMI +#endif +#ifdef __AVX512VBMI2__ + | (Q_UINT64_C(1) << 29) // CpuFeatureAVX512VBMI2 +#endif +#ifdef __GFNI__ + | (Q_UINT64_C(1) << 30) // CpuFeatureGFNI +#endif +#ifdef __VAES__ + | (Q_UINT64_C(1) << 31) // CpuFeatureVAES +#endif +#ifdef __AVX512VNNI__ + | (Q_UINT64_C(1) << 32) // CpuFeatureAVX512VNNI +#endif +#ifdef __AVX512BITALG__ + | (Q_UINT64_C(1) << 33) // CpuFeatureAVX512BITALG +#endif +#ifdef __AVX512VPOPCNTDQ__ + | (Q_UINT64_C(1) << 34) // CpuFeatureAVX512VPOPCNTDQ +#endif +#ifdef __AVX5124NNIW__ + | (Q_UINT64_C(1) << 35) // CpuFeatureAVX5124NNIW +#endif +#ifdef __AVX5124FMAPS__ + | (Q_UINT64_C(1) << 36) // CpuFeatureAVX5124FMAPS +#endif + ; + +QT_END_NAMESPACE + +#endif // QSIMD_X86_P_H diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index a83d8833cd..d3a6851e2c 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -254,118 +254,198 @@ inline RetType UnrollTailLoop<0>::exec(Number, RetType returnIfExited, Functor1, #endif #ifdef __SSE2__ +// Scans from \a ptr to \a end until \a maskval is non-zero. Returns true if +// the no non-zero was found. Returns false and updates \a ptr to point to the +// first 16-bit word that has any bit set (note: if the input is 8-bit, \a ptr +// may be updated to one byte short). static bool simdTestMask(const char *&ptr, const char *end, quint32 maskval) { -# if defined(__AVX2__) + auto updatePtr = [&](uint result) { + // found a character matching the mask + uint idx = qCountTrailingZeroBits(~result); + ptr += idx; + return false; + }; + +# if defined(__SSE4_1__) + __m128i mask; + auto updatePtrSimd = [&](__m128i data) { + __m128i masked = _mm_and_si128(mask, data); + __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128()); + uint result = _mm_movemask_epi8(comparison); + return updatePtr(result); + }; + +# if defined(__AVX2__) // AVX2 implementation: test 32 bytes at a time const __m256i mask256 = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(maskval)); while (ptr + 32 <= end) { __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr)); - if (!_mm256_testz_si256(mask256, data)) - return false; + if (!_mm256_testz_si256(mask256, data)) { + // found a character matching the mask + __m256i masked256 = _mm256_and_si256(mask256, data); + __m256i comparison256 = _mm256_cmpeq_epi16(masked256, _mm256_setzero_si256()); + return updatePtr(_mm256_movemask_epi8(comparison256)); + } ptr += 32; } - const __m128i mask = _mm256_castsi256_si128(mask256); -# elif defined(__SSE4_1__) + mask = _mm256_castsi256_si128(mask256); +# else // SSE 4.1 implementation: test 32 bytes at a time (two 16-byte // comparisons, unrolled) - const __m128i mask = _mm_set1_epi32(maskval); + mask = _mm_set1_epi32(maskval); while (ptr + 32 <= end) { __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); __m128i data2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 16)); if (!_mm_testz_si128(mask, data1)) - return false; + return updatePtrSimd(data1); + + ptr += 16; if (!_mm_testz_si128(mask, data2)) - return false; - ptr += 32; + return updatePtrSimd(data2); + ptr += 16; } -# endif -# if defined(__SSE4_1__) +# endif + // AVX2 and SSE4.1: final 16-byte comparison if (ptr + 16 <= end) { __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); if (!_mm_testz_si128(mask, data1)) - return false; + return updatePtrSimd(data1); ptr += 16; } + + // and final 8-byte comparison + if (ptr + 8 <= end) { + __m128i data1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr)); + if (!_mm_testz_si128(mask, data1)) + return updatePtrSimd(data1); + ptr += 8; + } + # else // SSE2 implementation: test 16 bytes at a time. const __m128i mask = _mm_set1_epi32(maskval); - while (ptr + 16 < end) { + while (ptr + 16 <= end) { __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); - __m128i masked = _mm_andnot_si128(mask, data); + __m128i masked = _mm_and_si128(mask, data); __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128()); - if (quint16(_mm_movemask_epi8(comparison)) != 0xffff) - return false; + quint16 result = _mm_movemask_epi8(comparison); + if (result != 0xffff) + return updatePtr(result); ptr += 16; } + + // and one 8-byte comparison + if (ptr + 8 <= end) { + __m128i data = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr)); + __m128i masked = _mm_and_si128(mask, data); + __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128()); + quint8 result = _mm_movemask_epi8(comparison); + if (result != 0xff) + return updatePtr(result); + ptr += 8; + } # endif return true; } #endif -bool QtPrivate::isAscii(QLatin1String s) Q_DECL_NOTHROW +// Note: ptr on output may be off by one and point to a preceding US-ASCII +// character. Usually harmless. +bool qt_is_ascii(const char *&ptr, const char *end) Q_DECL_NOTHROW { - const char *ptr = s.begin(); - const char *end = s.end(); - #if defined(__SSE2__) // Testing for the high bit can be done efficiently with just PMOVMSKB # if defined(__AVX2__) while (ptr + 32 <= end) { __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr)); quint32 mask = _mm256_movemask_epi8(data); - if (mask) + if (mask) { + uint idx = qCountTrailingZeroBits(mask); + ptr += idx; return false; + } ptr += 32; } # endif - while (ptr + 16 <= end) { __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); quint32 mask = _mm_movemask_epi8(data); - if (mask) + if (mask) { + uint idx = qCountTrailingZeroBits(mask); + ptr += idx; return false; + } ptr += 16; } + if (ptr + 8 <= end) { + __m128i data = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr)); + quint8 mask = _mm_movemask_epi8(data); + if (mask) { + uint idx = qCountTrailingZeroBits(mask); + ptr += idx; + return false; + } + ptr += 8; + } #endif while (ptr + 4 <= end) { quint32 data = qFromUnaligned<quint32>(ptr); - if (data & 0x80808080U) + if (data &= 0x80808080U) { + uint idx = qCountTrailingZeroBits(data); + ptr += idx / 8; return false; + } ptr += 4; } while (ptr != end) { - if (quint8(*ptr++) & 0x80) + if (quint8(*ptr) & 0x80) return false; + ++ptr; } return true; } -bool QtPrivate::isAscii(QStringView s) Q_DECL_NOTHROW +bool QtPrivate::isAscii(QLatin1String s) Q_DECL_NOTHROW { - const QChar *ptr = s.begin(); - const QChar *end = s.end(); + const char *ptr = s.begin(); + const char *end = s.end(); + + return qt_is_ascii(ptr, end); +} +static bool isAscii(const QChar *&ptr, const QChar *end) +{ #ifdef __SSE2__ const char *ptr8 = reinterpret_cast<const char *>(ptr); const char *end8 = reinterpret_cast<const char *>(end); - if (!simdTestMask(ptr8, end8, 0xff80ff80)) - return false; + bool ok = simdTestMask(ptr8, end8, 0xff80ff80); ptr = reinterpret_cast<const QChar *>(ptr8); + if (!ok) + return false; #endif while (ptr != end) { - if ((*ptr++).unicode() & 0xff80) + if (ptr->unicode() & 0xff80) return false; + ++ptr; } return true; } +bool QtPrivate::isAscii(QStringView s) Q_DECL_NOTHROW +{ + const QChar *ptr = s.begin(); + const QChar *end = s.end(); + + return isAscii(ptr, end); +} + bool QtPrivate::isLatin1(QStringView s) Q_DECL_NOTHROW { const QChar *ptr = s.begin(); @@ -439,11 +519,19 @@ void qt_from_latin1(ushort *dst, const char *str, size_t size) Q_DECL_NOTHROW #endif } - size = size % 16; + // we're going to read str[offset..offset+7] (8 bytes) + if (str + offset + 7 < e) { + const __m128i chunk = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(str + offset)); + const __m128i unpacked = _mm_unpacklo_epi8(chunk, _mm_setzero_si128()); + _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + offset), unpacked); + offset += 8; + } + + size = size % 8; dst += offset; str += offset; # if defined(Q_COMPILER_LAMBDA) && !defined(__OPTIMIZE_SIZE__) - return UnrollTailLoop<15>::exec(int(size), [=](int i) { dst[i] = (uchar)str[i]; }); + return UnrollTailLoop<7>::exec(int(size), [=](int i) { dst[i] = (uchar)str[i]; }); # endif #endif #if defined(__mips_dsp) @@ -457,86 +545,117 @@ void qt_from_latin1(ushort *dst, const char *str, size_t size) Q_DECL_NOTHROW #endif } -#if defined(__SSE2__) -static inline __m128i mergeQuestionMarks(__m128i chunk) +template <bool Checked> +static void qt_to_latin1_internal(uchar *dst, const ushort *src, qsizetype length) { +#if defined(__SSE2__) + uchar *e = dst + length; + qptrdiff offset = 0; + +# ifdef __AVX2__ + const __m256i questionMark256 = _mm256_broadcastw_epi16(_mm_cvtsi32_si128('?')); + const __m256i outOfRange256 = _mm256_broadcastw_epi16(_mm_cvtsi32_si128(0x100)); + const __m128i questionMark = _mm256_castsi256_si128(questionMark256); + const __m128i outOfRange = _mm256_castsi256_si128(outOfRange256); +# else const __m128i questionMark = _mm_set1_epi16('?'); + const __m128i outOfRange = _mm_set1_epi16(0x100); +# endif -# ifdef __SSE4_2__ - // compare the unsigned shorts for the range 0x0100-0xFFFF - // note on the use of _mm_cmpestrm: - // The MSDN documentation online (http://technet.microsoft.com/en-us/library/bb514080.aspx) - // says for range search the following: - // For each character c in a, determine whether b0 <= c <= b1 or b2 <= c <= b3 - // - // However, all examples on the Internet, including from Intel - // (see http://software.intel.com/en-us/articles/xml-parsing-accelerator-with-intel-streaming-simd-extensions-4-intel-sse4/) - // put the range to be searched first - // - // Disassembly and instruction-level debugging with GCC and ICC show - // that they are doing the right thing. Inverting the arguments in the - // instruction does cause a bunch of test failures. - - const __m128i rangeMatch = _mm_cvtsi32_si128(0xffff0100); - const __m128i offLimitMask = _mm_cmpestrm(rangeMatch, 2, chunk, 8, - _SIDD_UWORD_OPS | _SIDD_CMP_RANGES | _SIDD_UNIT_MASK); - - // replace the non-Latin 1 characters in the chunk with question marks - chunk = _mm_blendv_epi8(chunk, questionMark, offLimitMask); + auto mergeQuestionMarks = [=](__m128i chunk) { + // SSE has no compare instruction for unsigned comparison. +# ifdef __SSE4_1__ + // We use an unsigned uc = qMin(uc, 0x100) and then compare for equality. + chunk = _mm_min_epu16(chunk, outOfRange); + const __m128i offLimitMask = _mm_cmpeq_epi16(chunk, outOfRange); + chunk = _mm_blendv_epi8(chunk, questionMark, offLimitMask); # else - // SSE has no compare instruction for unsigned comparison. - // The variables must be shiffted + 0x8000 to be compared - const __m128i signedBitOffset = _mm_set1_epi16(short(0x8000)); - const __m128i thresholdMask = _mm_set1_epi16(short(0xff + 0x8000)); + // The variables must be shiffted + 0x8000 to be compared + const __m128i signedBitOffset = _mm_set1_epi16(short(0x8000)); + const __m128i thresholdMask = _mm_set1_epi16(short(0xff + 0x8000)); - const __m128i signedChunk = _mm_add_epi16(chunk, signedBitOffset); - const __m128i offLimitMask = _mm_cmpgt_epi16(signedChunk, thresholdMask); + const __m128i signedChunk = _mm_add_epi16(chunk, signedBitOffset); + const __m128i offLimitMask = _mm_cmpgt_epi16(signedChunk, thresholdMask); -# ifdef __SSE4_1__ - // replace the non-Latin 1 characters in the chunk with question marks - chunk = _mm_blendv_epi8(chunk, questionMark, offLimitMask); -# else - // offLimitQuestionMark contains '?' for each 16 bits that was off-limit - // the 16 bits that were correct contains zeros - const __m128i offLimitQuestionMark = _mm_and_si128(offLimitMask, questionMark); + // offLimitQuestionMark contains '?' for each 16 bits that was off-limit + // the 16 bits that were correct contains zeros + const __m128i offLimitQuestionMark = _mm_and_si128(offLimitMask, questionMark); - // correctBytes contains the bytes that were in limit - // the 16 bits that were off limits contains zeros - const __m128i correctBytes = _mm_andnot_si128(offLimitMask, chunk); + // correctBytes contains the bytes that were in limit + // the 16 bits that were off limits contains zeros + const __m128i correctBytes = _mm_andnot_si128(offLimitMask, chunk); - // merge offLimitQuestionMark and correctBytes to have the result - chunk = _mm_or_si128(correctBytes, offLimitQuestionMark); -# endif -# endif - return chunk; -} -#endif + // merge offLimitQuestionMark and correctBytes to have the result + chunk = _mm_or_si128(correctBytes, offLimitQuestionMark); -static void qt_to_latin1(uchar *dst, const ushort *src, int length) -{ -#if defined(__SSE2__) - uchar *e = dst + length; - qptrdiff offset = 0; + Q_UNUSED(outOfRange); +# endif + return chunk; + }; // we're going to write to dst[offset..offset+15] (16 bytes) for ( ; dst + offset + 15 < e; offset += 16) { +# if defined(__AVX2__) + __m256i chunk = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + offset)); + if (Checked) { + // See mergeQuestionMarks lambda above for details + chunk = _mm256_min_epu16(chunk, outOfRange256); + const __m256i offLimitMask = _mm256_cmpeq_epi16(chunk, outOfRange256); + chunk = _mm256_blendv_epi8(chunk, questionMark256, offLimitMask); + } + + const __m128i chunk2 = _mm256_extracti128_si256(chunk, 1); + const __m128i chunk1 = _mm256_castsi256_si128(chunk); +# else __m128i chunk1 = _mm_loadu_si128((const __m128i*)(src + offset)); // load - chunk1 = mergeQuestionMarks(chunk1); + if (Checked) + chunk1 = mergeQuestionMarks(chunk1); __m128i chunk2 = _mm_loadu_si128((const __m128i*)(src + offset + 8)); // load - chunk2 = mergeQuestionMarks(chunk2); + if (Checked) + chunk2 = mergeQuestionMarks(chunk2); +# endif // pack the two vector to 16 x 8bits elements const __m128i result = _mm_packus_epi16(chunk1, chunk2); _mm_storeu_si128((__m128i*)(dst + offset), result); // store } - length = length % 16; +# if !defined(__OPTIMIZE_SIZE__) + // we're going to write to dst[offset..offset+7] (8 bytes) + if (dst + offset + 7 < e) { + __m128i chunk = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + offset)); + if (Checked) + chunk = mergeQuestionMarks(chunk); + + // pack, where the upper half is ignored + const __m128i result = _mm_packus_epi16(chunk, chunk); + _mm_storel_epi64(reinterpret_cast<__m128i *>(dst + offset), result); + offset += 8; + } + + // we're going to write to dst[offset..offset+3] (4 bytes) + if (dst + offset + 3 < e) { + __m128i chunk = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(src + offset)); + if (Checked) + chunk = mergeQuestionMarks(chunk); + + // pack, we'll the upper three quarters + const __m128i result = _mm_packus_epi16(chunk, chunk); + qToUnaligned(_mm_cvtsi128_si32(result), dst + offset); + offset += 4; + } + + length = length % 4; dst += offset; src += offset; -# if defined(Q_COMPILER_LAMBDA) && !defined(__OPTIMIZE_SIZE__) - return UnrollTailLoop<15>::exec(length, [=](int i) { dst[i] = (src[i]>0xff) ? '?' : (uchar) src[i]; }); + return UnrollTailLoop<3>::exec(length, [=](int i) { + if (Checked) + dst[i] = (src[i]>0xff) ? '?' : (uchar) src[i]; + else + dst[i] = src[i]; + }); # endif #elif defined(__ARM_NEON__) // Refer to the documentation of the SSE2 implementation @@ -551,10 +670,12 @@ static void qt_to_latin1(uchar *dst, const ushort *src, int length) uint16x8_t chunk = vld1q_u16((uint16_t *)src); // load src += 8; - const uint16x8_t offLimitMask = vcgtq_u16(chunk, thresholdMask); // chunk > thresholdMask - const uint16x8_t offLimitQuestionMark = vandq_u16(offLimitMask, questionMark); // offLimitMask & questionMark - const uint16x8_t correctBytes = vbicq_u16(chunk, offLimitMask); // !offLimitMask & chunk - chunk = vorrq_u16(correctBytes, offLimitQuestionMark); // correctBytes | offLimitQuestionMark + if (Checked) { + const uint16x8_t offLimitMask = vcgtq_u16(chunk, thresholdMask); // chunk > thresholdMask + const uint16x8_t offLimitQuestionMark = vandq_u16(offLimitMask, questionMark); // offLimitMask & questionMark + const uint16x8_t correctBytes = vbicq_u16(chunk, offLimitMask); // !offLimitMask & chunk + chunk = vorrq_u16(correctBytes, offLimitQuestionMark); // correctBytes | offLimitQuestionMark + } const uint8x8_t result = vmovn_u16(chunk); // narrowing move->packing vst1_u8(dst, result); // store dst += 8; @@ -566,12 +687,25 @@ static void qt_to_latin1(uchar *dst, const ushort *src, int length) qt_toLatin1_mips_dsp_asm(dst, src, length); #else while (length--) { - *dst++ = (*src>0xff) ? '?' : (uchar) *src; + if (Checked) + *dst++ = (*src>0xff) ? '?' : (uchar) *src; + else + *dst++ = *src; ++src; } #endif } +static void qt_to_latin1(uchar *dst, const ushort *src, qsizetype length) +{ + qt_to_latin1_internal<true>(dst, src, length); +} + +void qt_to_latin1_unchecked(uchar *dst, const ushort *src, qsizetype length) +{ + qt_to_latin1_internal<false>(dst, src, length); +} + // Unicode case-insensitive comparison static int ucstricmp(const QChar *a, const QChar *ae, const QChar *b, const QChar *be) { @@ -836,12 +970,11 @@ static int ucstrncmp(const QChar *a, const uchar *c, size_t l) } } -# ifdef Q_PROCESSOR_X86_64 - enum { MaxTailLength = 7 }; +# if !defined(__OPTIMIZE_SIZE__) // we'll read uc[offset..offset+7] (16 bytes) and c[offset..offset+7] (8 bytes) if (uc + offset + 7 < e) { // same, but we're using an 8-byte load - __m128i chunk = _mm_cvtsi64_si128(qFromUnaligned<long long>(c + offset)); + __m128i chunk = _mm_loadl_epi64((const __m128i*)(c + offset)); __m128i secondHalf = _mm_unpacklo_epi8(chunk, nullmask); __m128i ucdata = _mm_loadu_si128((const __m128i*)(uc + offset)); @@ -856,17 +989,30 @@ static int ucstrncmp(const QChar *a, const uchar *c, size_t l) // still matched offset += 8; } -# else - // 32-bit, we can't do MOVQ to load 8 bytes - Q_UNUSED(nullmask); - enum { MaxTailLength = 15 }; -# endif + + enum { MaxTailLength = 3 }; + // we'll read uc[offset..offset+3] (8 bytes) and c[offset..offset+3] (4 bytes) + if (uc + offset + 3 < e) { + __m128i chunk = _mm_cvtsi32_si128(qFromUnaligned<int>(c + offset)); + __m128i secondHalf = _mm_unpacklo_epi8(chunk, nullmask); + + __m128i ucdata = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(uc + offset)); + __m128i result = _mm_cmpeq_epi8(secondHalf, ucdata); + uint mask = ~_mm_movemask_epi8(result); + if (uchar(mask)) { + // found a different character + uint idx = qCountTrailingZeroBits(mask); + return uc[offset + idx / 2] - c[offset + idx / 2]; + } + + // still matched + offset += 4; + } // reset uc and c uc += offset; c += offset; -# if !defined(__OPTIMIZE_SIZE__) const auto lambda = [=](size_t i) { return uc[i] - ushort(c[i]); }; return UnrollTailLoop<MaxTailLength>::exec(e - uc, 0, lambda, lambda); # endif @@ -930,14 +1076,12 @@ static int qt_compare_strings(QLatin1String lhs, QStringView rhs, Qt::CaseSensit static int qt_compare_strings(QLatin1String lhs, QLatin1String rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { + if (cs == Qt::CaseInsensitive) + return qstrnicmp(lhs.data(), lhs.size(), rhs.data(), rhs.size()); if (lhs.isEmpty()) return lencmp(0, rhs.size()); const auto l = std::min(lhs.size(), rhs.size()); - int r; - if (cs == Qt::CaseSensitive) - r = qstrncmp(lhs.data(), rhs.data(), l); - else - r = qstrnicmp(lhs.data(), rhs.data(), l); + int r = qstrncmp(lhs.data(), rhs.data(), l); return r ? r : lencmp(lhs.size(), rhs.size()); } @@ -1055,8 +1199,23 @@ static int findChar(const QChar *str, int len, QChar ch, int from, } } -# if defined(Q_COMPILER_LAMBDA) && !defined(__OPTIMIZE_SIZE__) - return UnrollTailLoop<7>::exec(e - n, -1, +# if !defined(__OPTIMIZE_SIZE__) + // we're going to read n[0..3] (8 bytes) + if (e - n > 3) { + __m128i data = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(n)); + __m128i result = _mm_cmpeq_epi16(data, mch); + uint mask = _mm_movemask_epi8(result); + if (uchar(mask)) { + // found a match + // same as: return n - s + _bit_scan_forward(mask) / 2 + return (reinterpret_cast<const char *>(n) - reinterpret_cast<const char *>(s) + + qCountTrailingZeroBits(mask)) >> 1; + } + + n += 4; + } + + return UnrollTailLoop<3>::exec(e - n, -1, [=](int i) { return n[i] == c; }, [=](int i) { return n - s + i; }); # endif @@ -4930,16 +5089,55 @@ bool QString::endsWith(QChar c, Qt::CaseSensitivity cs) const return qt_ends_with(*this, c, cs); } -static QByteArray qt_convert_to_latin1(QStringView string); +/*! + Returns \c true if the string only contains uppercase letters, + otherwise returns \c false. + \since 5.12 -QByteArray QString::toLatin1_helper(const QString &string) + \sa QChar::isUpper(), isLower() +*/ +bool QString::isUpper() const { - return qt_convert_to_latin1(string); + if (isEmpty()) + return false; + + const QChar *d = data(); + + for (int i = 0, max = size(); i < max; ++i) { + if (!d[i].isUpper()) + return false; + } + + return true; } -QByteArray QString::toLatin1_helper(const QChar *data, int length) +/*! + Returns \c true if the string only contains lowercase letters, + otherwise returns \c false. + \since 5.12 + + \sa QChar::isLower(), isUpper() + */ +bool QString::isLower() const +{ + if (isEmpty()) + return false; + + const QChar *d = data(); + + for (int i = 0, max = size(); i < max; ++i) { + if (!d[i].isLower()) + return false; + } + + return true; +} + +static QByteArray qt_convert_to_latin1(QStringView string); + +QByteArray QString::toLatin1_helper(const QString &string) { - return qt_convert_to_latin1(QStringView(data, length)); + return qt_convert_to_latin1(string); } /*! @@ -6195,11 +6393,10 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, Qt::CaseSensitive); #if defined(Q_OS_WIN) -# ifndef Q_OS_WINRT - int res = CompareString(GetUserDefaultLCID(), 0, (wchar_t*)data1, length1, (wchar_t*)data2, length2); -# else - int res = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, (LPCWSTR)data1, length1, (LPCWSTR)data2, length2, NULL, NULL, 0); -# endif + QString lhs = QString::fromRawData(data1, length1).normalized(QString::NormalizationForm_C); + QString rhs = QString::fromRawData(data2, length2).normalized(QString::NormalizationForm_C); + + int res = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, (LPWSTR)lhs.constData(), lhs.length(), (LPWSTR)rhs.constData(), rhs.length(), NULL, NULL, 0); switch (res) { case CSTR_LESS_THAN: @@ -7784,19 +7981,11 @@ QString QString::repeated(int times) const void qt_string_normalize(QString *data, QString::NormalizationForm mode, QChar::UnicodeVersion version, int from) { - bool simple = true; - const QChar *p = data->constData(); - int len = data->length(); - for (int i = from; i < len; ++i) { - if (p[i].unicode() >= 0x80) { - simple = false; - if (i > from) - from = i - 1; - break; - } - } - if (simple) + const QChar *p = data->constData() + from; + if (isAscii(p, p + data->length())) return; + if (p > data->constData() + from) + from = p - data->constData() - 1; // need one before the non-ASCII to perform NFC if (version == QChar::Unicode_Unassigned) { version = QChar::currentUnicodeVersion(); diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index f27f7efa2b..f18baf7065 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -409,6 +409,9 @@ public: bool endsWith(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool endsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; + bool isUpper() const; + bool isLower() const; + Q_REQUIRED_RESULT QString leftJustified(int width, QChar fill = QLatin1Char(' '), bool trunc = false) const; Q_REQUIRED_RESULT QString rightJustified(int width, QChar fill = QLatin1Char(' '), bool trunc = false) const; @@ -875,7 +878,6 @@ private: static QString fromUtf8_helper(const char *str, int size); static QString fromLocal8Bit_helper(const char *, int size); static QByteArray toLatin1_helper(const QString &); - static QByteArray toLatin1_helper(const QChar *data, int size); static QByteArray toLatin1_helper_inplace(QString &); static QByteArray toUtf8_helper(const QString &); static QByteArray toLocal8Bit_helper(const QChar *data, int size); diff --git a/src/corelib/tools/qtimezone.cpp b/src/corelib/tools/qtimezone.cpp index daa7dc1531..db6be581ec 100644 --- a/src/corelib/tools/qtimezone.cpp +++ b/src/corelib/tools/qtimezone.cpp @@ -66,11 +66,10 @@ static QTimeZonePrivate *newBackendTimeZone() return new QAndroidTimeZonePrivate(); #elif defined(Q_OS_UNIX) || defined(Q_OS_ANDROID_EMBEDDED) return new QTzTimeZonePrivate(); - // Registry based timezone backend not available on WinRT -#elif defined Q_OS_WIN - return new QWinTimeZonePrivate(); #elif QT_CONFIG(icu) return new QIcuTimeZonePrivate(); +#elif defined Q_OS_WIN + return new QWinTimeZonePrivate(); #else return new QUtcTimeZonePrivate(); #endif // System Locales @@ -93,11 +92,10 @@ static QTimeZonePrivate *newBackendTimeZone(const QByteArray &ianaId) return new QAndroidTimeZonePrivate(ianaId); #elif defined(Q_OS_UNIX) || defined(Q_OS_ANDROID_EMBEDDED) return new QTzTimeZonePrivate(ianaId); - // Registry based timezone backend not available on WinRT -#elif defined Q_OS_WIN - return new QWinTimeZonePrivate(ianaId); #elif QT_CONFIG(icu) return new QIcuTimeZonePrivate(ianaId); +#elif defined Q_OS_WIN + return new QWinTimeZonePrivate(ianaId); #else return new QUtcTimeZonePrivate(ianaId); #endif // System Locales @@ -822,8 +820,8 @@ bool QTimeZone::isTimeZoneIdAvailable(const QByteArray &ianaId) // IDs as availableTimeZoneIds() may be slow if (!QTimeZonePrivate::isValidId(ianaId)) return false; - const QList<QByteArray> tzIds = availableTimeZoneIds(); - return std::binary_search(tzIds.begin(), tzIds.end(), ianaId); + return QUtcTimeZonePrivate().isTimeZoneIdAvailable(ianaId) || + global_tz->backend->isTimeZoneIdAvailable(ianaId); } static QList<QByteArray> set_union(const QList<QByteArray> &l1, const QList<QByteArray> &l2) diff --git a/src/corelib/tools/qtimezoneprivate.cpp b/src/corelib/tools/qtimezoneprivate.cpp index 1a5135f103..2b6c2ea6a5 100644 --- a/src/corelib/tools/qtimezoneprivate.cpp +++ b/src/corelib/tools/qtimezoneprivate.cpp @@ -486,6 +486,13 @@ QByteArray QTimeZonePrivate::systemTimeZoneId() const return QByteArray(); } +bool QTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray& ianaId) const +{ + // Fall-back implementation, can be made faster in subclasses + const QList<QByteArray> tzIds = availableTimeZoneIds(); + return std::binary_search(tzIds.begin(), tzIds.end(), ianaId); +} + QList<QByteArray> QTimeZonePrivate::availableTimeZoneIds() const { return QList<QByteArray>(); @@ -864,6 +871,17 @@ QByteArray QUtcTimeZonePrivate::systemTimeZoneId() const return utcQByteArray(); } +bool QUtcTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const +{ + for (int i = 0; i < utcDataTableSize; ++i) { + const QUtcData *data = utcData(i); + if (utcId(data) == ianaId) { + return true; + } + } + return false; +} + QList<QByteArray> QUtcTimeZonePrivate::availableTimeZoneIds() const { QList<QByteArray> result; diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h index 4d357111f2..c9a5726216 100644 --- a/src/corelib/tools/qtimezoneprivate_p.h +++ b/src/corelib/tools/qtimezoneprivate_p.h @@ -128,6 +128,7 @@ public: virtual QByteArray systemTimeZoneId() const; + virtual bool isTimeZoneIdAvailable(const QByteArray &ianaId) const; virtual QList<QByteArray> availableTimeZoneIds() const; virtual QList<QByteArray> availableTimeZoneIds(QLocale::Country country) const; virtual QList<QByteArray> availableTimeZoneIds(int utcOffset) const; @@ -204,6 +205,7 @@ public: QByteArray systemTimeZoneId() const override; + bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override; QList<QByteArray> availableTimeZoneIds() const override; QList<QByteArray> availableTimeZoneIds(QLocale::Country country) const override; QList<QByteArray> availableTimeZoneIds(int utcOffset) const override; @@ -323,6 +325,7 @@ public: QByteArray systemTimeZoneId() const override; + bool isTimeZoneIdAvailable(const QByteArray &ianaId) const override; QList<QByteArray> availableTimeZoneIds() const override; QList<QByteArray> availableTimeZoneIds(QLocale::Country country) const override; diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 6a5df6272a..f1735a80e7 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -1098,6 +1098,11 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const return ianaId; } +bool QTzTimeZonePrivate::isTimeZoneIdAvailable(const QByteArray &ianaId) const +{ + return tzZones->contains(ianaId); +} + QList<QByteArray> QTzTimeZonePrivate::availableTimeZoneIds() const { QList<QByteArray> result = tzZones->keys(); diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp index 8bde07c710..ec91b7e8a8 100644 --- a/src/corelib/tools/qtimezoneprivate_win.cpp +++ b/src/corelib/tools/qtimezoneprivate_win.cpp @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE #ifndef Q_OS_WINRT +// The registry-based timezone backend is not available on WinRT, which falls back to equivalent APIs. #define QT_USE_REGISTRY_TIMEZONE 1 #endif diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 72224f280d..30f7edb5f2 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -159,16 +159,18 @@ qtConfig(timezone) { SOURCES += \ tools/qtimezone.cpp \ tools/qtimezoneprivate.cpp - !nacl:darwin: \ + !nacl:darwin: { SOURCES += tools/qtimezoneprivate_mac.mm - else: android:!android-embedded: \ + } else: android:!android-embedded: { SOURCES += tools/qtimezoneprivate_android.cpp - else: unix: \ + } else: unix: { SOURCES += tools/qtimezoneprivate_tz.cpp - else: win32: \ - SOURCES += tools/qtimezoneprivate_win.cpp - qtConfig(icu): \ + qtConfig(icu): SOURCES += tools/qtimezoneprivate_icu.cpp + } else: qtConfig(icu): { SOURCES += tools/qtimezoneprivate_icu.cpp + } else: win32: { + SOURCES += tools/qtimezoneprivate_win.cpp + } } qtConfig(datetimeparser) { @@ -179,7 +181,9 @@ qtConfig(datetimeparser) { qtConfig(regularexpression) { QMAKE_USE_PRIVATE += pcre2 - HEADERS += tools/qregularexpression.h + HEADERS += \ + tools/qregularexpression.h \ + tools/qregularexpression_p.h SOURCES += tools/qregularexpression.cpp } |