diff options
Diffstat (limited to 'src/corelib/tools')
47 files changed, 1391 insertions, 771 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 55af7256be..a04536b18b 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -63,6 +63,29 @@ QT_WARNING_POP static const QArrayData &qt_array_empty = qt_array[0]; static const QArrayData &qt_array_unsharable_empty = qt_array[1]; +static inline size_t calculateBlockSize(size_t &capacity, size_t objectSize, size_t headerSize, + uint options) +{ + // Calculate the byte size + // allocSize = objectSize * capacity + headerSize, but checked for overflow + // plus padded to grow in size + if (options & QArrayData::Grow) { + auto r = qCalculateGrowingBlockSize(capacity, objectSize, headerSize); + capacity = r.elementCount; + return r.size; + } else { + return qCalculateBlockSize(capacity, objectSize, headerSize); + } +} + +static QArrayData *reallocateData(QArrayData *header, size_t allocSize, uint options) +{ + header = static_cast<QArrayData *>(::realloc(header, allocSize)); + if (header) + header->capacityReserved = bool(options & QArrayData::CapacityReserved); + return header; +} + QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, size_t capacity, AllocationOptions options) Q_DECL_NOTHROW { @@ -91,18 +114,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, if (headerSize > size_t(MaxAllocSize)) return 0; - // Calculate the byte size - // allocSize = objectSize * capacity + headerSize, but checked for overflow - // plus padded to grow in size - size_t allocSize; - if (options & Grow) { - auto r = qCalculateGrowingBlockSize(capacity, objectSize, headerSize); - capacity = r.elementCount; - allocSize = r.size; - } else { - allocSize = qCalculateBlockSize(capacity, objectSize, headerSize); - } - + size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize)); if (header) { quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) @@ -122,6 +134,21 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, return header; } +QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize, size_t capacity, + AllocationOptions options) Q_DECL_NOTHROW +{ + Q_ASSERT(data); + Q_ASSERT(data->isMutable()); + Q_ASSERT(!data->ref.isShared()); + + size_t headerSize = sizeof(QArrayData); + size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); + QArrayData *header = static_cast<QArrayData *>(reallocateData(data, allocSize, options)); + if (header) + header->alloc = capacity; + return header; +} + void QArrayData::deallocate(QArrayData *data, size_t objectSize, size_t alignment) Q_DECL_NOTHROW { diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h index 5a369baf08..bc20932cca 100644 --- a/src/corelib/tools/qarraydata.h +++ b/src/corelib/tools/qarraydata.h @@ -115,6 +115,9 @@ struct Q_CORE_EXPORT QArrayData static QArrayData *allocate(size_t objectSize, size_t alignment, size_t capacity, AllocationOptions options = Default) Q_DECL_NOTHROW Q_REQUIRED_RESULT; + static QArrayData *reallocateUnaligned(QArrayData *data, size_t objectSize, + size_t newCapacity, AllocationOptions newOptions = Default) + Q_DECL_NOTHROW Q_REQUIRED_RESULT; static void deallocate(QArrayData *data, size_t objectSize, size_t alignment) Q_DECL_NOTHROW; @@ -222,6 +225,14 @@ struct QTypedArrayData Q_ALIGNOF(AlignmentDummy), capacity, options)); } + static QTypedArrayData *reallocateUnaligned(QTypedArrayData *data, size_t capacity, + AllocationOptions options = Default) + { + Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData)); + return static_cast<QTypedArrayData *>(QArrayData::reallocateUnaligned(data, sizeof(T), + capacity, options)); + } + static void deallocate(QArrayData *data) { Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData)); diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index 6bb8280ca8..ae83e6986e 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -145,7 +145,7 @@ struct QGenericArrayOps T *const begin = this->begin(); do { - new (begin + this->size) T(); + new (begin + this->size) T; } while (uint(++this->size) != newSize); } @@ -411,18 +411,18 @@ struct QArrayOpsSelector template <class T> struct QArrayOpsSelector<T, - typename QEnableIf< - !QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic - >::Type> + typename std::enable_if< + !QTypeInfoQuery<T>::isComplex && QTypeInfoQuery<T>::isRelocatable + >::type> { typedef QPodArrayOps<T> Type; }; template <class T> struct QArrayOpsSelector<T, - typename QEnableIf< - QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic - >::Type> + typename std::enable_if< + QTypeInfoQuery<T>::isComplex && QTypeInfoQuery<T>::isRelocatable + >::type> { typedef QMovableArrayOps<T> Type; }; diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 9298472305..329cc358d4 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -541,15 +541,40 @@ static const quint16 crc_tbl[16] = { Returns the CRC-16 checksum of the first \a len bytes of \a data. - The checksum is independent of the byte order (endianness). + The checksum is independent of the byte order (endianness) and will be + calculated accorded to the algorithm published in ISO 3309 (Qt::ChecksumIso3309). \note This function is a 16-bit cache conserving (16 entry table) implementation of the CRC-16-CCITT algorithm. */ - quint16 qChecksum(const char *data, uint len) { - quint16 crc = 0xffff; + return qChecksum(data, len, Qt::ChecksumIso3309); +} + +/*! + \relates QByteArray + \since 5.9 + + Returns the CRC-16 checksum of the first \a len bytes of \a data. + + The checksum is independent of the byte order (endianness) and will + be calculated accorded to the algorithm published in \a standard. + + \note This function is a 16-bit cache conserving (16 entry table) + implementation of the CRC-16-CCITT algorithm. +*/ +quint16 qChecksum(const char *data, uint len, Qt::ChecksumType standard) +{ + quint16 crc = 0x0000; + switch (standard) { + case Qt::ChecksumIso3309: + crc = 0xffff; + break; + case Qt::ChecksumItuV41: + crc = 0x6363; + break; + } uchar c; const uchar *p = reinterpret_cast<const uchar *>(data); while (len--) { @@ -558,7 +583,14 @@ quint16 qChecksum(const char *data, uint len) c >>= 4; crc = ((crc >> 4) & 0x0fff) ^ crc_tbl[((crc ^ c) & 15)]; } - return ~crc & 0xffff; + switch (standard) { + case Qt::ChecksumIso3309: + crc = ~crc; + break; + case Qt::ChecksumItuV41: + break; + } + return crc & 0xffff; } /*! @@ -663,6 +695,20 @@ QByteArray qCompress(const uchar* data, int nbytes, int compressionLevel) */ #ifndef QT_NO_COMPRESS +namespace { +struct QByteArrayDataDeleter +{ + static inline void cleanup(QTypedArrayData<char> *d) + { if (d) QTypedArrayData<char>::deallocate(d); } +}; +} + +static QByteArray invalidCompressedData() +{ + qWarning("qUncompress: Input data is corrupted"); + return QByteArray(); +} + QByteArray qUncompress(const uchar* data, int nbytes) { if (!data) { @@ -677,53 +723,29 @@ QByteArray qUncompress(const uchar* data, int nbytes) ulong expectedSize = uint((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3] )); ulong len = qMax(expectedSize, 1ul); - QScopedPointer<QByteArray::Data, QScopedPointerPodDeleter> d; + const ulong maxPossibleSize = MaxAllocSize - sizeof(QByteArray::Data); + if (Q_UNLIKELY(len >= maxPossibleSize)) { + // QByteArray does not support that huge size anyway. + return invalidCompressedData(); + } + + QScopedPointer<QByteArray::Data, QByteArrayDataDeleter> d(QByteArray::Data::allocate(expectedSize + 1)); + if (Q_UNLIKELY(d.data() == nullptr)) + return invalidCompressedData(); + d->size = expectedSize; forever { ulong alloc = len; - if (len >= (1u << 31u) - sizeof(QByteArray::Data)) { - //QByteArray does not support that huge size anyway. - qWarning("qUncompress: Input data is corrupted"); - return QByteArray(); - } - QByteArray::Data *p = static_cast<QByteArray::Data *>(::realloc(d.data(), sizeof(QByteArray::Data) + alloc + 1)); - if (!p) { - // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS - qWarning("qUncompress: could not allocate enough memory to uncompress data"); - return QByteArray(); - } - d.take(); // realloc was successful - d.reset(p); - d->offset = sizeof(QByteArrayData); - d->size = 0; // Shut up valgrind "uninitialized variable" warning int res = ::uncompress((uchar*)d->data(), &len, data+4, nbytes-4); switch (res) { case Z_OK: - if (len != alloc) { - if (len >= (1u << 31u) - sizeof(QByteArray::Data)) { - //QByteArray does not support that huge size anyway. - qWarning("qUncompress: Input data is corrupted"); - return QByteArray(); - } - QByteArray::Data *p = static_cast<QByteArray::Data *>(::realloc(d.data(), sizeof(QByteArray::Data) + len + 1)); - if (!p) { - // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS - qWarning("qUncompress: could not allocate enough memory to uncompress data"); - return QByteArray(); - } - d.take(); // realloc was successful - d.reset(p); - } - d->ref.initializeOwned(); + Q_ASSERT(len <= alloc); + Q_UNUSED(alloc); d->size = len; - d->alloc = uint(len) + 1u; - d->capacityReserved = false; - d->offset = sizeof(QByteArrayData); d->data()[len] = 0; - { QByteArrayDataPtr dataPtr = { d.take() }; return QByteArray(dataPtr); @@ -735,6 +757,17 @@ QByteArray qUncompress(const uchar* data, int nbytes) case Z_BUF_ERROR: len *= 2; + if (Q_UNLIKELY(len >= maxPossibleSize)) { + // QByteArray does not support that huge size anyway. + return invalidCompressedData(); + } else { + // grow the block + QByteArray::Data *p = QByteArray::Data::reallocateUnaligned(d.data(), len + 1); + if (Q_UNLIKELY(p == nullptr)) + return invalidCompressedData(); + d.take(); // don't free + d.reset(p); + } continue; case Z_DATA_ERROR: @@ -1707,19 +1740,8 @@ void QByteArray::reallocData(uint alloc, Data::AllocationOptions options) Data::deallocate(d); d = x; } else { - size_t blockSize; - if (options & Data::Grow) { - auto r = qCalculateGrowingBlockSize(alloc, sizeof(QChar), sizeof(Data)); - blockSize = r.size; - alloc = uint(r.elementCount); - } else { - blockSize = qCalculateBlockSize(alloc, sizeof(QChar), sizeof(Data)); - } - - Data *x = static_cast<Data *>(::realloc(d, blockSize)); + Data *x = Data::reallocateUnaligned(d, alloc, options); Q_CHECK_PTR(x); - x->alloc = alloc; - x->capacityReserved = (options & Data::CapacityReserved) ? 1 : 0; d = x; } } @@ -4365,12 +4387,41 @@ QByteArray QByteArray::fromHex(const QByteArray &hexEncoded) */ QByteArray QByteArray::toHex() const { - QByteArray hex(d->size * 2, Qt::Uninitialized); + return toHex('\0'); +} + +/*! \overload + \since 5.9 + + Returns a hex encoded copy of the byte array. The hex encoding uses the numbers 0-9 and + the letters a-f. + + If \a separator is not '\0', the separator character is inserted between the hex bytes. + + Example: + \code + QByteArray macAddress = QByteArray::fromHex("123456abcdef"); + macAddress.toHex(':'); // returns "12:34:56:ab:cd:ef" + macAddress.toHex(0); // returns "123456abcdef" + \endcode + + \sa fromHex() +*/ +QByteArray QByteArray::toHex(char separator) const +{ + if (!d->size) + return QByteArray(); + + const int length = separator ? (d->size * 3 - 1) : (d->size * 2); + QByteArray hex(length, Qt::Uninitialized); char *hexData = hex.data(); const uchar *data = (const uchar *)d->data(); - for (int i = 0; i < d->size; ++i) { - hexData[i*2] = QtMiscUtils::toHexLower(data[i] >> 4); - hexData[i*2+1] = QtMiscUtils::toHexLower(data[i] & 0xf); + for (int i = 0, o = 0; i < d->size; ++i) { + hexData[o++] = QtMiscUtils::toHexLower(data[i] >> 4); + hexData[o++] = QtMiscUtils::toHexLower(data[i] & 0xf); + + if ((separator) && (o < length)) + hexData[o++] = separator; } return hex; } diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 477402d6de..06a50e5990 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -105,8 +105,8 @@ Q_CORE_EXPORT int qvsnprintf(char *str, size_t n, const char *fmt, va_list ap); Q_CORE_EXPORT int qsnprintf(char *str, size_t n, const char *fmt, ...); // qChecksum: Internet checksum - -Q_CORE_EXPORT quint16 qChecksum(const char *s, uint len); +Q_CORE_EXPORT quint16 qChecksum(const char *s, uint len); // ### Qt 6: Remove +Q_CORE_EXPORT quint16 qChecksum(const char *s, uint len, Qt::ChecksumType standard); // ### Qt 6: Use Qt::ChecksumType standard = Qt::ChecksumIso3309 class QByteRef; class QString; @@ -140,8 +140,6 @@ struct QByteArrayDataPtr Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, sizeof(QByteArrayData)) \ /**/ -#if defined(Q_COMPILER_LAMBDA) - # define QByteArrayLiteral(str) \ ([]() -> QByteArray { \ enum { Size = sizeof(str) - 1 }; \ @@ -154,14 +152,6 @@ struct QByteArrayDataPtr }()) \ /**/ -#endif - -#ifndef QByteArrayLiteral -// no lambdas, not GCC, just return a temporary QByteArray - -# define QByteArrayLiteral(str) QByteArray(str, sizeof(str) - 1) -#endif - class Q_CORE_EXPORT QByteArray { private: @@ -252,7 +242,7 @@ public: void truncate(int pos); void chop(int n); -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) && !defined(Q_CLANG_QDOC) # if defined(Q_CC_GNU) // required due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61941 # pragma push_macro("Q_REQUIRED_RESULT") @@ -357,6 +347,7 @@ public: QByteArray toBase64(Base64Options options) const; QByteArray toBase64() const; // ### Qt6 merge with previous QByteArray toHex() const; + QByteArray toHex(char separator) const; // ### Qt6 merge with previous QByteArray toPercentEncoding(const QByteArray &exclude = QByteArray(), const QByteArray &include = QByteArray(), char percent = '%') const; diff --git a/src/corelib/tools/qbytearraymatcher.cpp b/src/corelib/tools/qbytearraymatcher.cpp index d06ca1292a..76af726ef9 100644 --- a/src/corelib/tools/qbytearraymatcher.cpp +++ b/src/corelib/tools/qbytearraymatcher.cpp @@ -323,4 +323,112 @@ int qFindByteArray( return -1; } +/*! + \class QStaticByteArrayMatcherBase + \since 5.9 + \internal + \brief Non-template base class of QStaticByteArrayMatcher. +*/ + +/*! + \class QStaticByteArrayMatcher + \since 5.9 + \inmodule QtCore + \brief The QStaticByteArrayMatcher class is a compile-time version of QByteArrayMatcher + + \ingroup tools + \ingroup string-processing + + This class is useful when you have a sequence of bytes that you + want to repeatedly match against some byte arrays (perhaps in a + loop), or when you want to search for the same sequence of bytes + multiple times in the same byte array. Using a matcher object and + indexIn() is faster than matching a plain QByteArray with + QByteArray::indexOf(), in particular if repeated matching takes place. + + Unlike QByteArrayMatcher, this class calculates the internal + representation at \e{compile-time}, if your compiler supports + C++14-level \c{constexpr} (C++11 is not sufficient), so it can + even benefit if you are doing one-off byte array matches. + + Create the QStaticByteArrayMatcher by calling qMakeStaticByteArrayMatcher(), + passing it the C string literal you want to search for. Store the return + value of that function in a \c{static const auto} variable, so you don't need + to pass the \c{N} template parameter explicitly: + + \code + static const auto matcher = qMakeStaticByteArrayMatcher("needle"); + \endcode + + Then call indexIn() on the QByteArray in which you want to search, just like + with QByteArrayMatcher. + + Since this class is designed to do all the up-front calculations at compile-time, + it does not offer a setPattern() method. + + \sa QByteArrayMatcher, QStringMatcher +*/ + +/*! + \fn int QStaticByteArrayMatcher::indexIn(const char *haystack, int hlen, int from = 0) const + + Searches the char string \a haystack, which has length \a hlen, from + byte position \a from (default 0, i.e. from the first byte), for + the byte array pattern() that was set in the constructor. + + Returns the position where the pattern() matched in \a haystack, or -1 if no match was found. +*/ + +/*! + \fn int QStaticByteArrayMatcher::indexIn(const QByteArray &haystack, int from = 0) const + + Searches the char string \a haystack, from byte position \a from + (default 0, i.e. from the first byte), for the byte array pattern() + that was set in the constructor. + + Returns the position where the pattern() matched in \a haystack, or -1 if no match was found. +*/ + +/*! + \fn QByteArray QStaticByteArrayMatcher::pattern() const + + Returns the byte array pattern that this byte array matcher will + search for. + + \sa setPattern() +*/ + +/*! + \internal +*/ +int QStaticByteArrayMatcherBase::indexOfIn(const char *needle, uint nlen, const char *haystack, int hlen, int from) const Q_DECL_NOTHROW +{ + if (from < 0) + from = 0; + return bm_find(reinterpret_cast<const uchar *>(haystack), hlen, from, + reinterpret_cast<const uchar *>(needle), nlen, m_skiptable.data); +} + +/*! + \fn QStaticByteArrayMatcher::QStaticByteArrayMatcher(const char (&pattern)[N]) + \internal +*/ + +/*! + \fn qMakeStaticByteArrayMatcher(const char (&pattern)[N]) + \since 5.9 + \relates QStaticByteArrayMatcher + + Return a QStaticByteArrayMatcher with the correct \c{N} determined + automatically from the \a pattern passed. + + To take full advantage of this function, assign the result to an + \c{auto} variable: + + \code + static const auto matcher = qMakeStaticByteArrayMatcher("needle"); + \endcode +*/ + + QT_END_NAMESPACE diff --git a/src/corelib/tools/qbytearraymatcher.h b/src/corelib/tools/qbytearraymatcher.h index aac62715a1..c1c0c3a660 100644 --- a/src/corelib/tools/qbytearraymatcher.h +++ b/src/corelib/tools/qbytearraymatcher.h @@ -83,6 +83,87 @@ private: }; }; +class QStaticByteArrayMatcherBase +{ + Q_DECL_ALIGN(16) + struct Skiptable { + uchar data[256]; + } m_skiptable; +protected: + explicit Q_DECL_RELAXED_CONSTEXPR QStaticByteArrayMatcherBase(const char *pattern, uint n) Q_DECL_NOTHROW + : m_skiptable(generate(pattern, n)) {} + // compiler-generated copy/more ctors/assignment operators are ok! + // compiler-generated dtor is ok! + + Q_CORE_EXPORT int indexOfIn(const char *needle, uint nlen, const char *haystack, int hlen, int from) const Q_DECL_NOTHROW; + +private: + static Q_DECL_RELAXED_CONSTEXPR Skiptable generate(const char *pattern, uint n) Q_DECL_NOTHROW + { + const auto uchar_max = (std::numeric_limits<uchar>::max)(); + uchar max = n > uchar_max ? uchar_max : n; + Skiptable table = { + // this verbose initialization code aims to avoid some opaque error messages + // even on powerful compilers such as GCC 5.3. Even though for GCC a loop + // format can be found that v5.3 groks, it's probably better to go with this + // for the time being: + { + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, max, + } + }; + pattern += n - max; + while (max--) + table.data[uchar(*pattern++)] = max; + return table; + } +}; + +QT_WARNING_PUSH +QT_WARNING_DISABLE_MSVC(4351) // MSVC 2013: "new behavior: elements of array ... will be default initialized" + // remove once we drop MSVC 2013 support +template <uint N> +class QStaticByteArrayMatcher : QStaticByteArrayMatcherBase +{ + char m_pattern[N]; + Q_STATIC_ASSERT_X(N > 2, "QStaticByteArrayMatcher makes no sense for finding a single-char pattern"); +public: + explicit Q_DECL_RELAXED_CONSTEXPR QStaticByteArrayMatcher(const char (&patternToMatch)[N]) Q_DECL_NOTHROW + : QStaticByteArrayMatcherBase(patternToMatch, N - 1), m_pattern() + { + for (uint i = 0; i < N; ++i) + m_pattern[i] = patternToMatch[i]; + } + + int indexIn(const QByteArray &haystack, int from = 0) const Q_DECL_NOTHROW + { return this->indexOfIn(m_pattern, N - 1, haystack.data(), haystack.size(), from); } + int indexIn(const char *haystack, int hlen, int from = 0) const Q_DECL_NOTHROW + { return this->indexOfIn(m_pattern, N - 1, haystack, hlen, from); } + + QByteArray pattern() const { return QByteArray(m_pattern, int(N - 1)); } +}; + +QT_WARNING_POP + +template <uint N> +Q_DECL_RELAXED_CONSTEXPR QStaticByteArrayMatcher<N> qMakeStaticByteArrayMatcher(const char (&pattern)[N]) Q_DECL_NOTHROW +{ return QStaticByteArrayMatcher<N>(pattern); } + QT_END_NAMESPACE #endif // QBYTEARRAYMATCHER_H diff --git a/src/corelib/tools/qchar.cpp b/src/corelib/tools/qchar.cpp index 5f918aab0f..1d3293e85e 100644 --- a/src/corelib/tools/qchar.cpp +++ b/src/corelib/tools/qchar.cpp @@ -101,7 +101,7 @@ QT_BEGIN_NAMESPACE In Qt, Unicode characters are 16-bit entities without any markup or structure. This class represents such an entity. It is lightweight, so it can be used everywhere. Most compilers treat - it like a \c{unsigned short}. + it like an \c{unsigned short}. QChar provides a full complement of testing/classification functions, converting to and from other formats, converting from @@ -424,7 +424,7 @@ QT_BEGIN_NAMESPACE \enum QChar::Direction This enum type defines the Unicode direction attributes. See the - \l{http://www.unicode.org/}{Unicode Standard} for a description + \l{http://www.unicode.org/reports/tr9/tr9-35.html#Table_Bidirectional_Character_Types}{Unicode Standard} for a description of the values. In order to conform to C/C++ naming conventions "Dir" is prepended diff --git a/src/corelib/tools/qcollator.h b/src/corelib/tools/qcollator.h index 5ff74fd69a..d81c7c85e3 100644 --- a/src/corelib/tools/qcollator.h +++ b/src/corelib/tools/qcollator.h @@ -69,7 +69,7 @@ public: protected: QCollatorSortKey(QCollatorSortKeyPrivate*); - QSharedDataPointer<QCollatorSortKeyPrivate> d; + QExplicitlySharedDataPointer<QCollatorSortKeyPrivate> d; private: QCollatorSortKey(); diff --git a/src/corelib/tools/qcollator_icu.cpp b/src/corelib/tools/qcollator_icu.cpp index 1e94798c5b..ad98a187c5 100644 --- a/src/corelib/tools/qcollator_icu.cpp +++ b/src/corelib/tools/qcollator_icu.cpp @@ -39,6 +39,7 @@ ****************************************************************************/ #include "qcollator_p.h" +#include "qlocale_p.h" #include "qstringlist.h" #include "qstring.h" @@ -56,7 +57,7 @@ void QCollatorPrivate::init() cleanup(); UErrorCode status = U_ZERO_ERROR; - QByteArray name = locale.bcp47Name().replace(QLatin1Char('-'), QLatin1Char('_')).toLatin1(); + QByteArray name = QLocalePrivate::get(locale)->bcp47Name('_'); collator = ucol_open(name.constData(), &status); if (U_FAILURE(status)) { qWarning("Could not create collator: %d", status); @@ -144,7 +145,7 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const string.size(), (uint8_t *)result.data(), result.size()); } result.truncate(size); - return QCollatorSortKey(new QCollatorSortKeyPrivate(result)); + return QCollatorSortKey(new QCollatorSortKeyPrivate(std::move(result))); } return QCollatorSortKey(new QCollatorSortKeyPrivate(QByteArray())); diff --git a/src/corelib/tools/qcollator_macx.cpp b/src/corelib/tools/qcollator_macx.cpp index b4d93e58d4..d468272430 100644 --- a/src/corelib/tools/qcollator_macx.cpp +++ b/src/corelib/tools/qcollator_macx.cpp @@ -38,9 +38,12 @@ ****************************************************************************/ #include "qcollator_p.h" +#include "qlocale_p.h" #include "qstringlist.h" #include "qstring.h" + #include <QtCore/private/qcore_mac_p.h> + #include <CoreFoundation/CoreFoundation.h> #include <CoreFoundation/CFLocale.h> @@ -53,7 +56,7 @@ void QCollatorPrivate::init() { cleanup(); LocaleRef localeRef; - int rc = LocaleRefFromLocaleString(locale.bcp47Name().toLocal8Bit(), &localeRef); + int rc = LocaleRefFromLocaleString(QLocalePrivate::get(locale)->bcp47Name().constData(), &localeRef); if (rc != 0) qWarning("couldn't initialize the locale"); @@ -128,7 +131,7 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const ret.size(), &actualSize, ret.data()); } ret[actualSize] = 0; - return QCollatorSortKey(new QCollatorSortKeyPrivate(ret)); + return QCollatorSortKey(new QCollatorSortKeyPrivate(std::move(ret))); } int QCollatorSortKey::compare(const QCollatorSortKey &key) const diff --git a/src/corelib/tools/qcollator_p.h b/src/corelib/tools/qcollator_p.h index 423ba0325a..c03a3431db 100644 --- a/src/corelib/tools/qcollator_p.h +++ b/src/corelib/tools/qcollator_p.h @@ -85,7 +85,7 @@ typedef QVector<wchar_t> CollatorKeyType; typedef int CollatorType; #endif -class Q_CORE_EXPORT QCollatorPrivate +class QCollatorPrivate { public: QAtomicInt ref; @@ -127,13 +127,14 @@ private: Q_DISABLE_COPY(QCollatorPrivate) }; -class Q_CORE_EXPORT QCollatorSortKeyPrivate : public QSharedData +class QCollatorSortKeyPrivate : public QSharedData { friend class QCollator; public: - QCollatorSortKeyPrivate(const CollatorKeyType &key) + template <typename...T> + explicit QCollatorSortKeyPrivate(T &&...args) : QSharedData() - , m_key(key) + , m_key(std::forward<T>(args)...) { } diff --git a/src/corelib/tools/qcollator_posix.cpp b/src/corelib/tools/qcollator_posix.cpp index da424970e6..42413a4a82 100644 --- a/src/corelib/tools/qcollator_posix.cpp +++ b/src/corelib/tools/qcollator_posix.cpp @@ -110,7 +110,7 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const } result.resize(size+1); result[size] = 0; - return QCollatorSortKey(new QCollatorSortKeyPrivate(result)); + return QCollatorSortKey(new QCollatorSortKeyPrivate(std::move(result))); } int QCollatorSortKey::compare(const QCollatorSortKey &otherKey) const diff --git a/src/corelib/tools/qcollator_win.cpp b/src/corelib/tools/qcollator_win.cpp index fcd8d069eb..bce896278e 100644 --- a/src/corelib/tools/qcollator_win.cpp +++ b/src/corelib/tools/qcollator_win.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qcollator_p.h" +#include "qlocale_p.h" #include "qstringlist.h" #include "qstring.h" @@ -61,7 +62,7 @@ void QCollatorPrivate::init() collator = 0; #ifndef USE_COMPARESTRINGEX - localeID = qt_inIsoNametoLCID(locale.bcp47Name().toUtf8().constData()); + localeID = qt_inIsoNametoLCID(QLocalePrivate::get(locale)->bcp47Name().constData()); #else localeName = locale.bcp47Name(); #endif @@ -146,7 +147,7 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const if (finalSize == 0) { qWarning() << "there were problems when generating the ::sortKey by LCMapStringW with error:" << GetLastError(); } - return QCollatorSortKey(new QCollatorSortKeyPrivate(ret)); + return QCollatorSortKey(new QCollatorSortKeyPrivate(std::move(ret))); } int QCollatorSortKey::compare(const QCollatorSortKey &otherKey) const diff --git a/src/corelib/tools/qdatetime.cpp b/src/corelib/tools/qdatetime.cpp index bf92be2dd4..bcdbc5af2a 100644 --- a/src/corelib/tools/qdatetime.cpp +++ b/src/corelib/tools/qdatetime.cpp @@ -929,7 +929,7 @@ QString QDate::toString(Qt::DateFormat format) const */ QString QDate::toString(const QString& format) const { - return QLocale::system().toString(*this, format); + return QLocale::system().toString(*this, format); // QLocale::c() ### Qt6 } #endif //QT_NO_DATESTRING @@ -1333,6 +1333,7 @@ QDate QDate::fromString(const QString &string, const QString &format) QDate date; #if QT_CONFIG(timezone) QDateTimeParser dt(QVariant::Date, QDateTimeParser::FromString); + // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format)) dt.fromString(string, &date, 0); #else @@ -1676,7 +1677,7 @@ QString QTime::toString(Qt::DateFormat format) const */ QString QTime::toString(const QString& format) const { - return QLocale::system().toString(*this, format); + return QLocale::system().toString(*this, format); // QLocale::c() ### Qt6 } #endif //QT_NO_DATESTRING /*! @@ -1970,7 +1971,7 @@ QTime QTime::fromString(const QString& string, Qt::DateFormat format) case Qt::ISODateWithMs: case Qt::TextDate: default: - return fromIsoTimeString(&string, format, 0); + return fromIsoTimeString(QStringRef(&string), format, 0); } } @@ -2030,6 +2031,7 @@ QTime QTime::fromString(const QString &string, const QString &format) QTime time; #if QT_CONFIG(timezone) QDateTimeParser dt(QVariant::Time, QDateTimeParser::FromString); + // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format)) dt.fromString(string, 0, &time); #else @@ -2285,10 +2287,11 @@ static bool qt_localtime(qint64 msecsSinceEpoch, QDate *localDate, QTime *localT tm local; bool valid = false; -#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) // localtime() is required to work as if tzset() was called before it. // localtime_r() does not have this requirement, so make an explicit call. + // The explicit call should also request the timezone info be re-parsed. qt_tzset(); +#if !defined(QT_NO_THREAD) && defined(_POSIX_THREAD_SAFE_FUNCTIONS) // Use the reentrant version of localtime() where available // as is thread-safe and doesn't use a shared static data area tm *res = 0; @@ -2574,6 +2577,13 @@ static inline Qt::TimeSpec getSpec(const QDateTimeData &d) return extractSpec(getStatus(d)); } +#if QT_CONFIG(timezone) +void QDateTimePrivate::setUtcOffsetByTZ(qint64 atMSecsSinceEpoch) +{ + m_offsetFromUtc = m_timeZone.d->offsetFromUtc(atMSecsSinceEpoch); +} +#endif + // Refresh the LocalTime validity and offset static void refreshDateTime(QDateTimeData &d) { @@ -2589,10 +2599,12 @@ static void refreshDateTime(QDateTimeData &d) #if QT_CONFIG(timezone) // If not valid time zone then is invalid if (spec == Qt::TimeZone) { - if (!d->m_timeZone.isValid()) + if (!d->m_timeZone.isValid()) { status &= ~QDateTimePrivate::ValidDateTime; - else - epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(msecs, d->m_timeZone, &testDate, &testTime); + } else { + epochMSecs = QDateTimePrivate::zoneMSecsToEpochMSecs(msecs, d->m_timeZone, extractDaylightStatus(status), &testDate, &testTime); + d->setUtcOffsetByTZ(epochMSecs); + } } #endif // timezone @@ -2916,11 +2928,13 @@ inline QDateTime::Data QDateTimePrivate::create(const QDate &toDate, const QTime } // Convert a TimeZone time expressed in zone msecs encoding into a UTC epoch msecs +// DST transitions are disambiguated by hint. inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QTimeZone &zone, + DaylightStatus hint, QDate *localDate, QTime *localTime) { // Get the effective data from QTimeZone - QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs); + QTimeZonePrivate::Data data = zone.d->dataForLocalTime(zoneMSecs, int(hint)); // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. if (data.atMSecsSinceEpoch >= 0) { @@ -3533,7 +3547,8 @@ qint64 QDateTime::toMSecsSinceEpoch() const #if !QT_CONFIG(timezone) return 0; #else - return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone); + return QDateTimePrivate::zoneMSecsToEpochMSecs(d->m_msecs, d->m_timeZone, + extractDaylightStatus(getStatus(d))); #endif } Q_UNREACHABLE(); @@ -3632,10 +3647,16 @@ void QDateTime::setMSecsSinceEpoch(qint64 msecs) // Docs state any LocalTime before 1970-01-01 will *not* have any DST applied // but all affected times afterwards will have DST applied. d.detach(); - if (msecs >= 0) + if (msecs >= 0) { + status = mergeDaylightStatus(status, + d->m_timeZone.d->isDaylightTime(msecs) + ? QDateTimePrivate::DaylightTime + : QDateTimePrivate::StandardTime); d->m_offsetFromUtc = d->m_timeZone.d->offsetFromUtc(msecs); - else + } else { + status = mergeDaylightStatus(status, QDateTimePrivate::StandardTime); d->m_offsetFromUtc = d->m_timeZone.d->standardTimeOffset(msecs); + } msecs = msecs + (d->m_offsetFromUtc * 1000); status = status | QDateTimePrivate::ValidDate @@ -3783,15 +3804,20 @@ QString QDateTime::toString(Qt::DateFormat format) const #ifndef QT_NO_TEXTDATE case Qt::TextDate: { const QPair<QDate, QTime> p = getDateTime(d); - const QDate &dt = p.first; - const QTime &tm = p.second; - //We cant use date.toString(Qt::TextDate) as we need to insert the time before the year - buf = QString::fromLatin1("%1 %2 %3 %4 %5").arg(dt.shortDayName(dt.dayOfWeek())) - .arg(dt.shortMonthName(dt.month())) - .arg(dt.day()) - .arg(tm.toString(Qt::TextDate)) - .arg(dt.year()); - if (timeSpec() != Qt::LocalTime) { + buf = p.first.toString(Qt::TextDate); + // Insert time between date's day and year: + buf.insert(buf.lastIndexOf(QLatin1Char(' ')), + QLatin1Char(' ') + p.second.toString(Qt::TextDate)); + // Append zone/offset indicator, as appropriate: + switch (timeSpec()) { + case Qt::LocalTime: + break; +# if QT_CONFIG(timezone) + case Qt::TimeZone: + buf += QLatin1Char(' ') + d->m_timeZone.abbreviation(*this); + break; +# endif + default: buf += QLatin1String(" GMT"); if (getSpec(d) == Qt::OffsetFromUTC) buf += toOffsetString(Qt::TextDate, offsetFromUtc()); @@ -3814,6 +3840,9 @@ QString QDateTime::toString(Qt::DateFormat format) const buf += QLatin1Char('Z'); break; case Qt::OffsetFromUTC: +#if QT_CONFIG(timezone) + case Qt::TimeZone: +#endif buf += toOffsetString(Qt::ISODate, offsetFromUtc()); break; default: @@ -3899,7 +3928,7 @@ QString QDateTime::toString(Qt::DateFormat format) const */ QString QDateTime::toString(const QString& format) const { - return QLocale::system().toString(*this, format); + return QLocale::system().toString(*this, format); // QLocale::c() ### Qt6 } #endif //QT_NO_DATESTRING @@ -3923,7 +3952,10 @@ static inline void massageAdjustedDateTime(const QDateTimeData &d, QDate *date, localMSecsToEpochMSecs(timeToMSecs(*date, *time), &status, date, time); #if QT_CONFIG(timezone) } else if (spec == Qt::TimeZone) { - QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time), d->m_timeZone, date, time); + QDateTimePrivate::zoneMSecsToEpochMSecs(timeToMSecs(*date, *time), + d->m_timeZone, + QDateTimePrivate::UnknownDaylightTime, + date, time); #endif // timezone } } @@ -4953,6 +4985,7 @@ QDateTime QDateTime::fromString(const QString &string, const QString &format) QDate date; QDateTimeParser dt(QVariant::DateTime, QDateTimeParser::FromString); + // dt.setDefaultLocale(QLocale::c()); ### Qt 6 if (dt.parseFormat(format) && dt.fromString(string, &date, &time)) return QDateTime(date, time); #else diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h index eb33dddbb7..4d30d4192b 100644 --- a/src/corelib/tools/qdatetime_p.h +++ b/src/corelib/tools/qdatetime_p.h @@ -134,11 +134,12 @@ public: #if QT_CONFIG(timezone) static qint64 zoneMSecsToEpochMSecs(qint64 msecs, const QTimeZone &zone, + DaylightStatus hint = UnknownDaylightTime, QDate *localDate = 0, QTime *localTime = 0); -#endif // timezone - static inline qint64 minJd() { return QDate::minJd(); } - static inline qint64 maxJd() { return QDate::maxJd(); } + // Inlined for its one caller in qdatetime.cpp + inline void setUtcOffsetByTZ(qint64 atMSecsSinceEpoch); +#endif // timezone }; QT_END_NAMESPACE diff --git a/src/corelib/tools/qeasingcurve.h b/src/corelib/tools/qeasingcurve.h index efed5d36ee..ba06de8f9e 100644 --- a/src/corelib/tools/qeasingcurve.h +++ b/src/corelib/tools/qeasingcurve.h @@ -125,7 +125,7 @@ private: friend Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QEasingCurve &); #endif }; -Q_DECLARE_TYPEINFO(QEasingCurve, Q_MOVABLE_TYPE); +Q_DECLARE_SHARED(QEasingCurve) #ifndef QT_NO_DEBUG_STREAM Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QEasingCurve &item); diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index abec9ebb79..334bd52f1e 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -199,14 +199,14 @@ static uint crc32(...) } #endif -static inline uint hash(const uchar *p, int len, uint seed) Q_DECL_NOTHROW +static inline uint hash(const uchar *p, size_t len, uint seed) Q_DECL_NOTHROW { uint h = seed; if (hasFastCrc32()) - return crc32(p, size_t(len), h); + return crc32(p, len, h); - for (int i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) h = 31 * h + p[i]; return h; @@ -217,14 +217,14 @@ uint qHashBits(const void *p, size_t len, uint seed) Q_DECL_NOTHROW return hash(static_cast<const uchar*>(p), int(len), seed); } -static inline uint hash(const QChar *p, int len, uint seed) Q_DECL_NOTHROW +static inline uint hash(const QChar *p, size_t len, uint seed) Q_DECL_NOTHROW { uint h = seed; if (hasFastCrc32()) - return crc32(p, size_t(len), h); + return crc32(p, len, h); - for (int i = 0; i < len; ++i) + for (size_t i = 0; i < len; ++i) h = 31 * h + p[i].unicode(); return h; @@ -232,23 +232,24 @@ static inline uint hash(const QChar *p, int len, uint seed) Q_DECL_NOTHROW uint qHash(const QByteArray &key, uint seed) Q_DECL_NOTHROW { - return hash(reinterpret_cast<const uchar *>(key.constData()), key.size(), seed); + return hash(reinterpret_cast<const uchar *>(key.constData()), size_t(key.size()), seed); } uint qHash(const QString &key, uint seed) Q_DECL_NOTHROW { - return hash(key.unicode(), key.size(), seed); + return hash(key.unicode(), size_t(key.size()), seed); } uint qHash(const QStringRef &key, uint seed) Q_DECL_NOTHROW { - return hash(key.unicode(), key.size(), seed); + return hash(key.unicode(), size_t(key.size()), seed); } uint qHash(const QBitArray &bitArray, uint seed) Q_DECL_NOTHROW { int m = bitArray.d.size() - 1; - uint result = hash(reinterpret_cast<const uchar *>(bitArray.d.constData()), qMax(0, m), seed); + uint result = hash(reinterpret_cast<const uchar *>(bitArray.d.constData()), + size_t(qMax(0, m)), seed); // deal with the last 0 to 7 bits manually, because we can't trust that // the padding is initialized to 0 in bitArray.d @@ -260,7 +261,7 @@ uint qHash(const QBitArray &bitArray, uint seed) Q_DECL_NOTHROW uint qHash(QLatin1String key, uint seed) Q_DECL_NOTHROW { - return hash(reinterpret_cast<const uchar *>(key.data()), key.size(), seed); + return hash(reinterpret_cast<const uchar *>(key.data()), size_t(key.size()), seed); } /*! @@ -324,7 +325,7 @@ static uint qt_create_qhash_seed() /* The QHash seed itself. */ -Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed = Q_BASIC_ATOMIC_INITIALIZER(-1); +static QBasicAtomicInt qt_qhash_seed = Q_BASIC_ATOMIC_INITIALIZER(-1); /*! \internal diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 66b5e75a1a..c59f789cb2 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -432,6 +432,7 @@ public: typedef const Key *pointer; typedef const Key &reference; + key_iterator() = default; explicit key_iterator(const_iterator o) : i(o) { } const Key &operator*() const { return i.key(); } diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 5995be47a9..e2706de9ee 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -126,6 +126,7 @@ class QList public: struct MemoryLayout : std::conditional< + // must stay isStatic until ### Qt 6 for BC reasons (don't use !isRelocatable)! QTypeInfo<T>::isStatic || QTypeInfo<T>::isLarge, QListData::IndirectLayout, typename std::conditional< diff --git a/src/corelib/tools/qlocale.cpp b/src/corelib/tools/qlocale.cpp index c183224c82..bde4bf553e 100644 --- a/src/corelib/tools/qlocale.cpp +++ b/src/corelib/tools/qlocale.cpp @@ -972,7 +972,7 @@ QLocale::NumberOptions QLocale::numberOptions() const */ QString QLocale::quoteString(const QString &str, QuotationStyle style) const { - return quoteString(&str, style); + return quoteString(QStringRef(&str), style); } /*! @@ -2058,6 +2058,8 @@ QString QLocale::toString(double i, char f, int prec) const flags |= QLocaleData::ThousandsGroup; if (!(d->m_numberOptions & OmitLeadingZeroInExponent)) flags |= QLocaleData::ZeroPadExponent; + if (d->m_numberOptions & IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; return d->m_data->doubleToString(i, prec, form, -1, flags); } @@ -2819,7 +2821,7 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q reinterpret_cast<ushort *>(digits.data())[i] += z; } - bool always_show_decpt = (flags & Alternate || flags & ForcePoint); + bool always_show_decpt = (flags & ForcePoint); switch (form) { case DFExponent: { num_str = exponentForm(_zero, decimal, exponential, group, plus, minus, @@ -2834,7 +2836,7 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q break; } case DFSignificantDigits: { - PrecisionMode mode = (flags & Alternate) ? + PrecisionMode mode = (flags & AddTrailingZeroes) ? PMSignificantDigits : PMChopTrailingZeros; int cutoff = precision < 0 ? 6 : precision; @@ -2889,7 +2891,7 @@ QString QLocaleData::doubleToString(const QChar _zero, const QChar plus, const Q num_str.prepend(QLatin1Char(' ')); if (flags & QLocaleData::CapitalEorX) - num_str = num_str.toUpper(); + num_str = std::move(num_str).toUpper(); return num_str; } @@ -2939,7 +2941,7 @@ QString QLocaleData::longLongToString(const QChar zero, const QChar group, for (int i = num_str.length()/* - cnt_thousand_sep*/; i < precision; ++i) num_str.prepend(base == 10 ? zero : QChar::fromLatin1('0')); - if ((flags & Alternate || flags & ShowBase) + if ((flags & ShowBase) && base == 8 && (num_str.isEmpty() || num_str[0].unicode() != QLatin1Char('0'))) num_str.prepend(QLatin1Char('0')); @@ -2960,10 +2962,10 @@ QString QLocaleData::longLongToString(const QChar zero, const QChar group, --num_pad_chars; // leave space for optional '0x' in hex form - if (base == 16 && (flags & Alternate || flags & ShowBase)) + if (base == 16 && (flags & ShowBase)) num_pad_chars -= 2; // leave space for optional '0b' in binary form - else if (base == 2 && (flags & Alternate || flags & ShowBase)) + else if (base == 2 && (flags & ShowBase)) num_pad_chars -= 2; for (int i = 0; i < num_pad_chars; ++i) @@ -2971,11 +2973,11 @@ QString QLocaleData::longLongToString(const QChar zero, const QChar group, } if (flags & CapitalEorX) - num_str = num_str.toUpper(); + num_str = std::move(num_str).toUpper(); - if (base == 16 && (flags & Alternate || flags & ShowBase)) + if (base == 16 && (flags & ShowBase)) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x")); - if (base == 2 && (flags & Alternate || flags & ShowBase)) + if (base == 2 && (flags & ShowBase)) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b")); // add sign @@ -3024,7 +3026,7 @@ QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group, if (zeroPadding > 0) num_str.prepend(QString(zeroPadding, resultZero)); - if ((flags & Alternate || flags & ShowBase) + if ((flags & ShowBase) && base == 8 && (num_str.isEmpty() || num_str.at(0).unicode() != QLatin1Char('0'))) num_str.prepend(QLatin1Char('0')); @@ -3039,10 +3041,10 @@ QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group, int num_pad_chars = width - num_str.length(); // leave space for optional '0x' in hex form - if (base == 16 && flags & Alternate) + if (base == 16 && flags & ShowBase) num_pad_chars -= 2; // leave space for optional '0b' in binary form - else if (base == 2 && flags & Alternate) + else if (base == 2 && flags & ShowBase) num_pad_chars -= 2; if (num_pad_chars > 0) @@ -3050,11 +3052,11 @@ QString QLocaleData::unsLongLongToString(const QChar zero, const QChar group, } if (flags & CapitalEorX) - num_str = num_str.toUpper(); + num_str = std::move(num_str).toUpper(); - if (base == 16 && (flags & Alternate || flags & ShowBase)) + if (base == 16 && flags & ShowBase) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0X" : "0x")); - else if (base == 2 && (flags & Alternate || flags & ShowBase)) + else if (base == 2 && flags & ShowBase) num_str.prepend(QLatin1String(flags & UppercaseBase ? "0B" : "0b")); // add sign @@ -3115,25 +3117,37 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOpti out = in.toLatin1(); else break; + } else if (out == '.') { + // Fail if more than one decimal point or point after e + if (decpt_idx != -1 || exponent_idx != -1) + return false; + decpt_idx = idx; + } else if (out == 'e' || out == 'E') { + exponent_idx = idx; } if (number_options & QLocale::RejectLeadingZeroInExponent) { - if (out == 'e' || out == 'E') { - exponent_idx = idx; - } else if (exponent_idx != -1) { - if (out >= '1' && out <= '9') - exponent_idx = -1; // leading digit is not 0, forget exponent_idx - else if (out == '0' && idx < l - 1) + if (exponent_idx != -1 && out == '0' && idx < l - 1) { + // After the exponent there can only be '+', '-' or digits. + // If we find a '0' directly after some non-digit, then that is a leading zero. + if (result->last() < '0' || result->last() > '9') return false; } } + if (number_options & QLocale::RejectTrailingZeroesAfterDot) { + // If we've seen a decimal point and the last character after the exponent is 0, then + // that is a trailing zero. + if (decpt_idx >= 0 && idx == exponent_idx && result->last() == '0') + return false; + } + if (!(number_options & QLocale::RejectGroupSeparator)) { if (start_of_digits_idx == -1 && out >= '0' && out <= '9') { start_of_digits_idx = idx; } else if (out == ',') { - // Don't allow group chars after the decimal point - if (decpt_idx != -1) + // Don't allow group chars after the decimal point or exponent + if (decpt_idx != -1 || exponent_idx != -1) return false; // check distance from the last separator or from the beginning of the digits @@ -3150,12 +3164,6 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOpti ++idx; continue; } else if (out == '.' || out == 'e' || out == 'E') { - // Fail if more than one decimal point - if (out == '.' && decpt_idx != -1) - return false; - if (decpt_idx == -1) - decpt_idx = idx; - // check distance from the last separator // ### FIXME: Some locales allow other groupings! See https://en.wikipedia.org/wiki/Thousands_separator if (last_separator_idx != -1 && idx - last_separator_idx != 4) @@ -3181,6 +3189,12 @@ bool QLocaleData::numberToCLocale(const QChar *str, int len, QLocale::NumberOpti return false; } + if (number_options & QLocale::RejectTrailingZeroesAfterDot) { + // In decimal form, the last character can be a trailing zero if we've seen a decpt. + if (decpt_idx != -1 && exponent_idx == -1 && result->last() == '0') + return false; + } + result->append('\0'); return idx == l; } diff --git a/src/corelib/tools/qlocale.h b/src/corelib/tools/qlocale.h index 657fce9fa1..bd89e48234 100644 --- a/src/corelib/tools/qlocale.h +++ b/src/corelib/tools/qlocale.h @@ -897,7 +897,9 @@ public: OmitGroupSeparator = 0x01, RejectGroupSeparator = 0x02, OmitLeadingZeroInExponent = 0x04, - RejectLeadingZeroInExponent = 0x08 + RejectLeadingZeroInExponent = 0x08, + IncludeTrailingZeroesAfterDot = 0x10, + RejectTrailingZeroesAfterDot = 0x20 }; Q_DECLARE_FLAGS(NumberOptions, NumberOption) diff --git a/src/corelib/tools/qlocale.qdoc b/src/corelib/tools/qlocale.qdoc index 8c5711fb5e..d419172356 100644 --- a/src/corelib/tools/qlocale.qdoc +++ b/src/corelib/tools/qlocale.qdoc @@ -949,7 +949,8 @@ setNumberOptions(). \value DefaultNumberOptions This option represents the default behavior, with - group separators and with one leading zero in single digit exponents. + group separators, with one leading zero in single digit exponents, and + without trailing zeroes after the decimal dot. \value OmitGroupSeparator If this option is set, the number-to-string functions will not insert group separators in their return values. The default is to insert group separators. @@ -964,6 +965,14 @@ functions will fail if they encounter an exponent padded with zeroes when parsing a floating point number in scientific notation. The default is to accept such padding. + \value IncludeTrailingZeroesAfterDot If this option is set, the number-to-string + functions will pad numbers with zeroes to the requested precision in "g" + or "most concise" mode, even if the number of significant digits is lower + than the requested precision. The default is to omit trailing zeroes. + \value RejectTrailingZeroesAfterDot If this option is set, the string-to-number + functions will fail if they encounter trailing zeroes after the decimal + dot when parsing a number in scientific or decimal representation. The + default is to accept trailing zeroes. \sa setNumberOptions(), numberOptions() */ diff --git a/src/corelib/tools/qlocale_mac.mm b/src/corelib/tools/qlocale_mac.mm index 8587716446..c5519bfabf 100644 --- a/src/corelib/tools/qlocale_mac.mm +++ b/src/corelib/tools/qlocale_mac.mm @@ -43,9 +43,11 @@ #include "qvariant.h" #include "qdatetime.h" -#if !defined(QWS) && defined(Q_OS_MAC) -# include "private/qcore_mac_p.h" -# include <CoreFoundation/CoreFoundation.h> +#ifdef Q_OS_DARWIN +#include "qtimezone.h" +#include "private/qcore_mac_p.h" +#include <CoreFoundation/CoreFoundation.h> +QT_REQUIRE_CONFIG(timezone); #endif QT_BEGIN_NAMESPACE @@ -121,16 +123,7 @@ static QString macDayName(int day, bool short_format) static QString macDateToString(const QDate &date, bool short_format) { - CFGregorianDate macGDate; - macGDate.year = date.year(); - macGDate.month = date.month(); - macGDate.day = date.day(); - macGDate.hour = 0; - macGDate.minute = 0; - macGDate.second = 0.0; - QCFType<CFDateRef> myDate - = CFDateCreate(0, CFGregorianDateGetAbsoluteTime(macGDate, - QCFType<CFTimeZoneRef>(CFTimeZoneCopyDefault()))); + QCFType<CFDateRef> myDate = QDateTime(date, QTime()).toCFDate(); QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent(); CFDateFormatterStyle style = short_format ? kCFDateFormatterShortStyle : kCFDateFormatterLongStyle; QCFType<CFDateFormatterRef> myFormatter @@ -142,19 +135,7 @@ static QString macDateToString(const QDate &date, bool short_format) static QString macTimeToString(const QTime &time, bool short_format) { - CFGregorianDate macGDate; - // Assume this is local time and the current date - QDate dt = QDate::currentDate(); - macGDate.year = dt.year(); - macGDate.month = dt.month(); - macGDate.day = dt.day(); - macGDate.hour = time.hour(); - macGDate.minute = time.minute(); - macGDate.second = time.second(); - QCFType<CFDateRef> myDate - = CFDateCreate(0, CFGregorianDateGetAbsoluteTime(macGDate, - QCFType<CFTimeZoneRef>(CFTimeZoneCopyDefault()))); - + QCFType<CFDateRef> myDate = QDateTime(QDate::currentDate(), time).toCFDate(); QCFType<CFLocaleRef> mylocale = CFLocaleCopyCurrent(); CFDateFormatterStyle style = short_format ? kCFDateFormatterShortStyle : kCFDateFormatterLongStyle; QCFType<CFDateFormatterRef> myFormatter = CFDateFormatterCreate(kCFAllocatorDefault, diff --git a/src/corelib/tools/qlocale_p.h b/src/corelib/tools/qlocale_p.h index 20eff8fd64..f7adb021b6 100644 --- a/src/corelib/tools/qlocale_p.h +++ b/src/corelib/tools/qlocale_p.h @@ -193,7 +193,7 @@ public: enum Flags { NoFlags = 0, - Alternate = 0x01, + AddTrailingZeroes = 0x01, ZeroPadded = 0x02, LeftAdjusted = 0x04, BlankBeforePositive = 0x08, @@ -204,7 +204,7 @@ public: ShowBase = 0x80, UppercaseBase = 0x100, ZeroPadExponent = 0x200, - ForcePoint = Alternate + ForcePoint = 0x400 }; enum NumberMode { IntegerMode, DoubleStandardMode, DoubleScientificMode }; @@ -332,6 +332,9 @@ public: return retval; } + static QLocalePrivate *get(QLocale &l) { return l.d; } + static const QLocalePrivate *get(const QLocale &l) { return l.d; } + QChar decimal() const { return QChar(m_data->m_decimal); } QChar group() const { return QChar(m_data->m_group); } QChar list() const { return QChar(m_data->m_list); } diff --git a/src/corelib/tools/qlocale_win.cpp b/src/corelib/tools/qlocale_win.cpp index b5f97b5fe8..2475859abd 100644 --- a/src/corelib/tools/qlocale_win.cpp +++ b/src/corelib/tools/qlocale_win.cpp @@ -691,7 +691,7 @@ QString QSystemLocalePrivate::winToQtFormat(const QString &sys_fmt) if (text == QLatin1String("'")) result += QLatin1String("''"); else - result += QString(QLatin1Char('\'') + text + QLatin1Char('\'')); + result += QLatin1Char('\'') + text + QLatin1Char('\''); continue; } @@ -1037,7 +1037,7 @@ static QString winIso639LangName(LPWSTR id) if (!lang_code.isEmpty()) { const char *endptr; bool ok; - QByteArray latin1_lang_code = lang_code.toLatin1(); + QByteArray latin1_lang_code = std::move(lang_code).toLatin1(); int i = qstrtoull(latin1_lang_code, &endptr, 16, &ok); if (ok && *endptr == '\0') { switch (i) { @@ -1120,15 +1120,12 @@ static QByteArray getWinLocaleName(LPWSTR id) id = lcName; } #endif // Q_OS_WINRT - QString resultuage = winIso639LangName(id); + QString resultusage = winIso639LangName(id); QString country = winIso3116CtryName(id); - result = resultuage.toLatin1(); - if (!country.isEmpty()) { - result += '_'; - result += country.toLatin1(); - } + if (!country.isEmpty()) + resultusage += QLatin1Char('_') + country; - return result; + return std::move(resultusage).toLatin1(); } Q_CORE_EXPORT QLocale qt_localeFromLCID(LCID id) diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h index da77ba4458..a3b11eddcf 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -99,10 +99,10 @@ struct Q_CORE_EXPORT QMapNodeBase void setParent(QMapNodeBase *pp) { p = (p & Mask) | quintptr(pp); } template <typename T> - static typename QtPrivate::QEnableIf<QTypeInfo<T>::isComplex>::Type + static typename std::enable_if<QTypeInfo<T>::isComplex>::type callDestructorIfNecessary(T &t) Q_DECL_NOTHROW { Q_UNUSED(t); t.~T(); } // Q_UNUSED: silence MSVC unused 't' warning template <typename T> - static typename QtPrivate::QEnableIf<!QTypeInfo<T>::isComplex>::Type + static typename std::enable_if<!QTypeInfo<T>::isComplex>::type callDestructorIfNecessary(T &) Q_DECL_NOTHROW {} }; @@ -533,6 +533,7 @@ public: typedef const Key *pointer; typedef const Key &reference; + key_iterator() = default; explicit key_iterator(const_iterator o) : i(o) { } const Key &operator*() const { return i.key(); } diff --git a/src/corelib/tools/qregularexpression.cpp b/src/corelib/tools/qregularexpression.cpp index 4a30daa72c..88b696f53a 100644 --- a/src/corelib/tools/qregularexpression.cpp +++ b/src/corelib/tools/qregularexpression.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2015 Giuseppe D'Angelo <dangelog@gmail.com>. -** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> +** Copyright (C) 2016 Giuseppe D'Angelo <dangelog@gmail.com>. +** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -45,7 +45,7 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qhashfunctions.h> -#include <QtCore/qmutex.h> +#include <QtCore/qreadwritelock.h> #include <QtCore/qvector.h> #include <QtCore/qstringlist.h> #include <QtCore/qdebug.h> @@ -54,7 +54,9 @@ #include <QtCore/qatomic.h> #include <QtCore/qdatastream.h> -#include <pcre.h> +#define PCRE2_CODE_UNIT_WIDTH 16 + +#include <pcre2.h> QT_BEGIN_NAMESPACE @@ -443,19 +445,25 @@ QT_BEGIN_NAMESPACE Other differences are outlined below. - \section2 Exact matching + \section2 Porting from QRegExp::exactMatch() QRegExp::exactMatch() in Qt 4 served two purposes: it exactly matched a regular expression against a subject string, and it implemented partial - matching. In fact, if an exact match was not found, one could still find - out how much of the subject string was matched by the regular expression - by calling QRegExp::matchedLength(). If the returned length was equal - to the subject string's length, then one could desume that a partial match - was found. + matching. - QRegularExpression supports partial matching explicitly by means of the - appropriate MatchType. If instead you simply want to be sure that the - subject string matches the regular expression exactly, you can wrap the + \section3 Porting from QRegExp's Exact Matching + + Exact matching indicates whether the regular expression matches the entire + subject string. For example, the classes yield on the subject string \c{"abc123"}: + + \table + \header \li \li QRegExp::exactMatch() \li QRegularExpressionMatch::hasMatch() + \row \li \c{"\\d+"} \li \b false \li \b true + \row \li \c{"[a-z]+\\d+"} \li \b true \li \b true + \endtable + + Exact matching is not reflected in QRegularExpression. If you want to be + sure that the subject string matches the regular expression exactly, you can wrap the pattern between a couple of anchoring expressions. Simply putting the pattern between the \c{^} and the \c{$} anchors is enough in most cases: @@ -477,6 +485,17 @@ QT_BEGIN_NAMESPACE Note the usage of the non-capturing group in order to preserve the meaning of the branch operator inside the pattern. + \section3 Porting from QRegExp's Partial Matching + + When using QRegExp::exactMatch(), if an exact match was not found, one + could still find out how much of the subject string was matched by the + regular expression by calling QRegExp::matchedLength(). If the returned length + was equal to the subject string's length, then one could conclude that a partial + match was found. + + QRegularExpression supports partial matching explicitly by means of the + appropriate MatchType. + \section2 Global matching Due to limitations of the QRegExp API it was impossible to implement global @@ -548,7 +567,7 @@ QT_BEGIN_NAMESPACE \inmodule QtCore \reentrant - \brief The QRegularExpressionMatch class provides the results of matching + \brief The QRegularExpressionMatch class provides the results of a matching a QRegularExpression against a string. \since 5.0 @@ -789,19 +808,19 @@ static int convertToPcreOptions(QRegularExpression::PatternOptions patternOption int options = 0; if (patternOptions & QRegularExpression::CaseInsensitiveOption) - options |= PCRE_CASELESS; + options |= PCRE2_CASELESS; if (patternOptions & QRegularExpression::DotMatchesEverythingOption) - options |= PCRE_DOTALL; + options |= PCRE2_DOTALL; if (patternOptions & QRegularExpression::MultilineOption) - options |= PCRE_MULTILINE; + options |= PCRE2_MULTILINE; if (patternOptions & QRegularExpression::ExtendedPatternSyntaxOption) - options |= PCRE_EXTENDED; + options |= PCRE2_EXTENDED; if (patternOptions & QRegularExpression::InvertedGreedinessOption) - options |= PCRE_UNGREEDY; + options |= PCRE2_UNGREEDY; if (patternOptions & QRegularExpression::DontCaptureOption) - options |= PCRE_NO_AUTO_CAPTURE; + options |= PCRE2_NO_AUTO_CAPTURE; if (patternOptions & QRegularExpression::UseUnicodePropertiesOption) - options |= PCRE_UCP; + options |= PCRE2_UCP; return options; } @@ -814,7 +833,9 @@ static int convertToPcreOptions(QRegularExpression::MatchOptions matchOptions) int options = 0; if (matchOptions & QRegularExpression::AnchoredMatchOption) - options |= PCRE_ANCHORED; + options |= PCRE2_ANCHORED; + if (matchOptions & QRegularExpression::DontCheckSubjectStringMatchOption) + options |= PCRE2_NO_UTF_CHECK; return options; } @@ -856,20 +877,16 @@ struct QRegularExpressionPrivate : QSharedData QRegularExpression::PatternOptions patternOptions; QString pattern; - // *All* of the following members are set managed while holding this mutex, + // *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). - // On the other hand, after the compilation and studying, - // it's safe to *use* (i.e. read) them from multiple threads at the same time. - // Therefore, doMatch doesn't need to lock this mutex. - QMutex mutex; + mutable QReadWriteLock mutex; - // The PCRE pointers are reference-counted by the QRegularExpressionPrivate + // 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 - pcre16 *compiledPattern; - QAtomicPointer<pcre16_extra> studyData; - const char *errorString; + pcre2_code_16 *compiledPattern; + int errorCode; int errorOffset; int capturingCount; unsigned int usedCount; @@ -884,8 +901,7 @@ struct QRegularExpressionMatchPrivate : QSharedData int subjectStart, int subjectLength, QRegularExpression::MatchType matchType, - QRegularExpression::MatchOptions matchOptions, - int capturingCount = 0); + QRegularExpression::MatchOptions matchOptions); QRegularExpressionMatch nextMatch() const; @@ -934,10 +950,13 @@ QRegularExpression::QRegularExpression(QRegularExpressionPrivate &dd) \internal */ QRegularExpressionPrivate::QRegularExpressionPrivate() - : patternOptions(0), pattern(), + : QSharedData(), + patternOptions(0), + pattern(), mutex(), - compiledPattern(0), studyData(0), - errorString(0), errorOffset(-1), + compiledPattern(0), + errorCode(0), + errorOffset(-1), capturingCount(0), usedCount(0), usingCrLfNewlines(false), @@ -964,13 +983,16 @@ QRegularExpressionPrivate::~QRegularExpressionPrivate() */ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPrivate &other) : QSharedData(other), - patternOptions(other.patternOptions), pattern(other.pattern), + patternOptions(other.patternOptions), + pattern(other.pattern), mutex(), - compiledPattern(0), studyData(0), - errorString(0), - errorOffset(-1), capturingCount(0), + compiledPattern(0), + errorCode(0), + errorOffset(-1), + capturingCount(0), usedCount(0), - usingCrLfNewlines(false), isDirty(true) + usingCrLfNewlines(false), + isDirty(true) { } @@ -979,14 +1001,13 @@ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPri */ void QRegularExpressionPrivate::cleanCompiledPattern() { - pcre16_free(compiledPattern); - pcre16_free_study(studyData.load()); - usedCount = 0; + pcre2_code_free_16(compiledPattern); compiledPattern = 0; - studyData.store(0); - usingCrLfNewlines = false; + errorCode = 0; errorOffset = -1; capturingCount = 0; + usedCount = 0; + usingCrLfNewlines = false; } /*! @@ -994,7 +1015,7 @@ void QRegularExpressionPrivate::cleanCompiledPattern() */ void QRegularExpressionPrivate::compilePattern() { - QMutexLocker lock(&mutex); + const QWriteLocker lock(&mutex); if (!isDirty) return; @@ -1003,18 +1024,23 @@ void QRegularExpressionPrivate::compilePattern() cleanCompiledPattern(); int options = convertToPcreOptions(patternOptions); - options |= PCRE_UTF16; + options |= PCRE2_UTF; - int errorCode; - compiledPattern = pcre16_compile2(pattern.utf16(), options, - &errorCode, &errorString, &errorOffset, 0); + PCRE2_SIZE patternErrorOffset; + compiledPattern = pcre2_compile_16(pattern.utf16(), + pattern.length(), + options, + &errorCode, + &patternErrorOffset, + NULL); - if (!compiledPattern) + if (!compiledPattern) { + errorOffset = static_cast<int>(patternErrorOffset); return; - - Q_ASSERT(errorCode == 0); - Q_ASSERT(studyData.load() == 0); // studying (=>optimizing) is always done later - errorOffset = -1; + } else { + // ignore whatever PCRE2 wrote into errorCode -- leave it to 0 to mean "no error" + errorCode = 0; + } getPatternInfo(); } @@ -1025,53 +1051,31 @@ void QRegularExpressionPrivate::compilePattern() void QRegularExpressionPrivate::getPatternInfo() { Q_ASSERT(compiledPattern); - Q_ASSERT(studyData.load() == 0); - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_CAPTURECOUNT, &capturingCount); + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_CAPTURECOUNT, &capturingCount); // detect the settings for the newline - unsigned long int patternNewlineSetting; - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_OPTIONS, &patternNewlineSetting); - patternNewlineSetting &= PCRE_NEWLINE_CR | PCRE_NEWLINE_LF | PCRE_NEWLINE_CRLF - | PCRE_NEWLINE_ANY | PCRE_NEWLINE_ANYCRLF; - if (patternNewlineSetting == 0) { + unsigned int patternNewlineSetting; + if (pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_NEWLINE, &patternNewlineSetting) != 0) { // no option was specified in the regexp, grab PCRE build defaults - int pcreNewlineSetting; - pcre16_config(PCRE_CONFIG_NEWLINE, &pcreNewlineSetting); - switch (pcreNewlineSetting) { - case 13: - patternNewlineSetting = PCRE_NEWLINE_CR; break; - case 10: - patternNewlineSetting = PCRE_NEWLINE_LF; break; - case 3338: // (13<<8 | 10) - patternNewlineSetting = PCRE_NEWLINE_CRLF; break; - case -2: - patternNewlineSetting = PCRE_NEWLINE_ANYCRLF; break; - case -1: - patternNewlineSetting = PCRE_NEWLINE_ANY; break; - default: - qWarning("QRegularExpressionPrivate::compilePattern(): " - "PCRE_CONFIG_NEWLINE returned an unknown newline"); - break; - } + pcre2_config_16(PCRE2_CONFIG_NEWLINE, &patternNewlineSetting); } - usingCrLfNewlines = (patternNewlineSetting == PCRE_NEWLINE_CRLF) || - (patternNewlineSetting == PCRE_NEWLINE_ANY) || - (patternNewlineSetting == PCRE_NEWLINE_ANYCRLF); + usingCrLfNewlines = (patternNewlineSetting == PCRE2_NEWLINE_CRLF) || + (patternNewlineSetting == PCRE2_NEWLINE_ANY) || + (patternNewlineSetting == PCRE2_NEWLINE_ANYCRLF); - int hasJOptionChanged; - pcre16_fullinfo(compiledPattern, 0, PCRE_INFO_JCHANGED, &hasJOptionChanged); - if (hasJOptionChanged) { - qWarning("QRegularExpressionPrivate::getPatternInfo(): the pattern '%s'\n" - " is using the (?J) option; duplicate capturing group names are not supported by Qt", + unsigned int hasJOptionChanged; + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_JCHANGED, &hasJOptionChanged); + if (Q_UNLIKELY(hasJOptionChanged)) { + qWarning("QRegularExpressionPrivate::getPatternInfo(): the pattern '%s'\n is using the (?J) option; duplicate capturing group names are not supported by Qt", qPrintable(pattern)); } } /* - Simple "smartpointer" wrapper around a pcre_jit_stack, to be used with + Simple "smartpointer" wrapper around a pcre2_jit_stack_16, to be used with QThreadStorage. */ class QPcreJitStackPointer @@ -1086,7 +1090,7 @@ public: { // The default JIT stack size in PCRE is 32K, // we allocate from 32K up to 512K. - stack = pcre16_jit_stack_alloc(32*1024, 512*1024); + stack = pcre2_jit_stack_create_16(32 * 1024, 512 * 1024, NULL); } /*! \internal @@ -1094,10 +1098,10 @@ public: ~QPcreJitStackPointer() { if (stack) - pcre16_jit_stack_free(stack); + pcre2_jit_stack_free_16(stack); } - pcre16_jit_stack *stack; + pcre2_jit_stack_16 *stack; }; Q_GLOBAL_STATIC(QThreadStorage<QPcreJitStackPointer *>, jitStacks) @@ -1105,7 +1109,7 @@ Q_GLOBAL_STATIC(QThreadStorage<QPcreJitStackPointer *>, jitStacks) /*! \internal */ -static pcre16_jit_stack *qtPcreCallback(void *) +static pcre2_jit_stack_16 *qtPcreCallback(void *) { if (jitStacks()->hasLocalData()) return jitStacks()->localData()->stack; @@ -1135,53 +1139,32 @@ static bool isJitEnabled() /*! \internal - The purpose of the function is to call pcre16_study (which allows some - optimizations to be performed, including JIT-compiling the pattern), and - setting the studyData member variable to the result of the study. 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). + The purpose of the function is to call pcre2_jit_compile_16, which + JIT-compiles the pattern. - Notice that although the method is protected by a mutex, one thread may - invoke this function and return immediately (i.e. not study the pattern, - leaving studyData to NULL); but before calling pcre16_exec to perform the - match, another thread performs the studying and sets studyData to something - else. Although the assignment to studyData is itself atomic, the release of - the memory pointed by studyData isn't. Therefore, we work on a local copy - (localStudyData) before using storeRelease on studyData. In doMatch there's - the corresponding loadAcquire. + 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). */ void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option) { Q_ASSERT(compiledPattern); - QMutexLocker lock(&mutex); + static const bool enableJit = isJitEnabled(); - if (studyData.load()) // already optimized + if (!enableJit) return; + const QWriteLocker lock(&mutex); + if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count)) return; - static const bool enableJit = isJitEnabled(); - - int studyOptions = 0; - if (enableJit) - studyOptions |= (PCRE_STUDY_JIT_COMPILE | PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE | PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE); - - const char *err; - pcre16_extra * const localStudyData = pcre16_study(compiledPattern, studyOptions, &err); - - if (localStudyData && localStudyData->flags & PCRE_EXTRA_EXECUTABLE_JIT) - pcre16_assign_jit_stack(localStudyData, qtPcreCallback, 0); - - if (!localStudyData && err) - qWarning("QRegularExpressionPrivate::optimizePattern(): pcre_study failed: %s", err); - - studyData.storeRelease(localStudyData); + pcre2_jit_compile_16(compiledPattern, PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD); } /*! @@ -1197,7 +1180,7 @@ int QRegularExpressionPrivate::captureIndexForName(const QString &name) const if (!compiledPattern) return -1; - int index = pcre16_get_stringnumber(compiledPattern, name.utf16()); + int index = pcre2_substring_number_from_name_16(compiledPattern, name.utf16()); if (index >= 0) return index; @@ -1207,24 +1190,25 @@ int QRegularExpressionPrivate::captureIndexForName(const QString &name) const /*! \internal - This is a simple wrapper for pcre16_exec for handling the case in which the + This is a simple wrapper for pcre2_match_16 for handling the case in which the JIT runs out of memory. In that case, we allocate a thread-local JIT stack - and re-run pcre16_exec. + and re-run pcre2_match_16. */ -static int pcre16SafeExec(const pcre16 *code, const pcre16_extra *extra, - const unsigned short *subject, int length, - int startOffset, int options, - int *ovector, int ovecsize) +static int safe_pcre2_match_16(const pcre2_code_16 *code, + const unsigned short *subject, int length, + int startOffset, int options, + pcre2_match_data_16 *matchData, + pcre2_match_context_16 *matchContext) { - int result = pcre16_exec(code, extra, subject, length, - startOffset, options, ovector, ovecsize); + int result = pcre2_match_16(code, subject, length, + startOffset, options, matchData, matchContext); - if (result == PCRE_ERROR_JIT_STACKLIMIT && !jitStacks()->hasLocalData()) { + if (result == PCRE2_ERROR_JIT_STACKLIMIT && !jitStacks()->hasLocalData()) { QPcreJitStackPointer *p = new QPcreJitStackPointer; jitStacks()->setLocalData(p); - result = pcre16_exec(code, extra, subject, length, - startOffset, options, ovector, ovecsize); + result = pcre2_match_16(code, subject, length, + startOffset, options, matchData, matchContext); } return result; @@ -1273,29 +1257,24 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString QRegularExpression re(*const_cast<QRegularExpressionPrivate *>(this)); + QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, + subjectStart, subjectLength, + matchType, matchOptions); + if (offset < 0 || offset > subjectLength) - return new QRegularExpressionMatchPrivate(re, subject, subjectStart, subjectLength, matchType, matchOptions); + return priv; - if (!compiledPattern) { + if (Q_UNLIKELY(!compiledPattern)) { qWarning("QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object"); - return new QRegularExpressionMatchPrivate(re, subject, subjectStart, subjectLength, matchType, matchOptions); + return priv; } // skip optimizing and doing the actual matching if NoMatch type was requested if (matchType == QRegularExpression::NoMatch) { - QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, - subjectStart, subjectLength, - matchType, matchOptions); priv->isValid = true; return priv; } - // capturingCount doesn't include the implicit "0" capturing group - QRegularExpressionMatchPrivate *priv = new QRegularExpressionMatchPrivate(re, subject, - subjectStart, subjectLength, - matchType, matchOptions, - capturingCount + 1); - if (!(patternOptions & QRegularExpression::DontAutomaticallyOptimizeOption)) { const OptimizePatternOption optimizePatternOption = (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption) @@ -1306,22 +1285,15 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString const_cast<QRegularExpressionPrivate *>(this)->optimizePattern(optimizePatternOption); } - // work with a local copy of the study data, as we are running pcre_exec - // potentially more than once, and we don't want to run call it - // with different study data - const pcre16_extra * const currentStudyData = studyData.loadAcquire(); - int pcreOptions = convertToPcreOptions(matchOptions); if (matchType == QRegularExpression::PartialPreferCompleteMatch) - pcreOptions |= PCRE_PARTIAL_SOFT; + pcreOptions |= PCRE2_PARTIAL_SOFT; else if (matchType == QRegularExpression::PartialPreferFirstMatch) - pcreOptions |= PCRE_PARTIAL_HARD; + pcreOptions |= PCRE2_PARTIAL_HARD; - if (checkSubjectStringOption == DontCheckSubjectString - || matchOptions & QRegularExpression::DontCheckSubjectStringMatchOption) { - pcreOptions |= PCRE_NO_UTF16_CHECK; - } + if (checkSubjectStringOption == DontCheckSubjectString) + pcreOptions |= PCRE2_NO_UTF_CHECK; bool previousMatchWasEmpty = false; if (previous && previous->hasMatch && @@ -1329,25 +1301,28 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString previousMatchWasEmpty = true; } - int * const captureOffsets = priv->capturedOffsets.data(); - const int captureOffsetsCount = priv->capturedOffsets.size(); + pcre2_match_context_16 *matchContext = pcre2_match_context_create_16(NULL); + pcre2_jit_stack_assign_16(matchContext, &qtPcreCallback, NULL); + pcre2_match_data_16 *matchData = pcre2_match_data_create_from_pattern_16(compiledPattern, NULL); const unsigned short * const subjectUtf16 = subject.utf16() + subjectStart; int result; + QReadLocker lock(&mutex); + if (!previousMatchWasEmpty) { - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions, + matchData, matchContext); } else { - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions | PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions | PCRE2_NOTEMPTY_ATSTART | PCRE2_ANCHORED, + matchData, matchContext); - if (result == PCRE_ERROR_NOMATCH) { + if (result == PCRE2_ERROR_NOMATCH) { ++offset; if (usingCrLfNewlines @@ -1360,13 +1335,15 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString ++offset; } - result = pcre16SafeExec(compiledPattern, currentStudyData, - subjectUtf16, subjectLength, - offset, pcreOptions, - captureOffsets, captureOffsetsCount); + result = safe_pcre2_match_16(compiledPattern, + subjectUtf16, subjectLength, + offset, pcreOptions, + matchData, matchContext); } } + lock.unlock(); + #ifdef QREGULAREXPRESSION_DEBUG qDebug() << "Matching" << pattern << "against" << subject << "starting at" << subjectStart << "len" << subjectLength @@ -1386,10 +1363,10 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString priv->capturedOffsets.resize(result * 2); } else { // no match, partial match or error - priv->hasPartialMatch = (result == PCRE_ERROR_PARTIAL); - priv->isValid = (result == PCRE_ERROR_NOMATCH || result == PCRE_ERROR_PARTIAL); + priv->hasPartialMatch = (result == PCRE2_ERROR_PARTIAL); + priv->isValid = (result == PCRE2_ERROR_NOMATCH || result == PCRE2_ERROR_PARTIAL); - if (result == PCRE_ERROR_PARTIAL) { + if (result == PCRE2_ERROR_PARTIAL) { // partial match: // leave the start and end capture offsets (i.e. cap(0)) priv->capturedCount = 1; @@ -1401,6 +1378,35 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString } } + // copy the captured substrings offsets, if any + if (priv->capturedCount) { + PCRE2_SIZE *ovector = pcre2_get_ovector_pointer_16(matchData); + int * const capturedOffsets = priv->capturedOffsets.data(); + + for (int i = 0; i < priv->capturedCount * 2; ++i) + capturedOffsets[i] = static_cast<int>(ovector[i]); + + // For partial matches, PCRE2 and PCRE1 differ in behavior when lookbehinds + // are involved. PCRE2 reports the real begin of the match and the maximum + // used lookbehind as distinct information; PCRE1 instead automatically + // adjusted ovector[0] to include the maximum lookbehind. + // + // For instance, given the pattern "\bstring\b", and the subject "a str": + // * PCRE1 reports partial, capturing " str" + // * PCRE2 reports partial, capturing "str" with a lookbehind of 1 + // + // To keep behavior, emulate PCRE1 here. + // (Eventually, we could expose the lookbehind info in a future patch.) + if (result == PCRE2_ERROR_PARTIAL) { + unsigned int maximumLookBehind; + pcre2_pattern_info_16(compiledPattern, PCRE2_INFO_MAXLOOKBEHIND, &maximumLookBehind); + capturedOffsets[0] -= maximumLookBehind; + } + } + + pcre2_match_data_free_16(matchData); + pcre2_match_context_free_16(matchContext); + return priv; } @@ -1412,19 +1418,13 @@ QRegularExpressionMatchPrivate::QRegularExpressionMatchPrivate(const QRegularExp int subjectStart, int subjectLength, QRegularExpression::MatchType matchType, - QRegularExpression::MatchOptions matchOptions, - int capturingCount) + QRegularExpression::MatchOptions matchOptions) : regularExpression(re), subject(subject), subjectStart(subjectStart), subjectLength(subjectLength), matchType(matchType), matchOptions(matchOptions), capturedCount(0), hasMatch(false), hasPartialMatch(false), isValid(false) { - Q_ASSERT(capturingCount >= 0); - if (capturingCount > 0) { - const int captureOffsetsCount = capturingCount * 3; - capturedOffsets.resize(captureOffsetsCount); - } } @@ -1632,13 +1632,13 @@ QStringList QRegularExpression::namedCaptureGroups() const // contains one ushort followed by the name, NUL terminated. // The ushort is the numerical index of the name in the pattern. // The length of each entry is namedCapturingTableEntrySize. - ushort *namedCapturingTable; - int namedCapturingTableEntryCount; - int namedCapturingTableEntrySize; + PCRE2_SPTR16 *namedCapturingTable; + unsigned int namedCapturingTableEntryCount; + unsigned int namedCapturingTableEntrySize; - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMETABLE, &namedCapturingTable); - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMECOUNT, &namedCapturingTableEntryCount); - pcre16_fullinfo(d->compiledPattern, 0, PCRE_INFO_NAMEENTRYSIZE, &namedCapturingTableEntrySize); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMETABLE, &namedCapturingTable); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMECOUNT, &namedCapturingTableEntryCount); + pcre2_pattern_info_16(d->compiledPattern, PCRE2_INFO_NAMEENTRYSIZE, &namedCapturingTableEntrySize); QStringList result; @@ -1647,9 +1647,9 @@ QStringList QRegularExpression::namedCaptureGroups() const for (int i = 0; i < d->capturingCount + 1; ++i) result.append(QString()); - for (int i = 0; i < namedCapturingTableEntryCount; ++i) { - const ushort * const currentNamedCapturingTableRow = namedCapturingTable + - namedCapturingTableEntrySize * i; + for (unsigned int i = 0; i < namedCapturingTableEntryCount; ++i) { + const ushort * const currentNamedCapturingTableRow = + reinterpret_cast<const ushort *>(namedCapturingTable) + namedCapturingTableEntrySize * i; const int index = *currentNamedCapturingTableRow; result[index] = QString::fromUtf16(currentNamedCapturingTableRow + 1); @@ -1680,8 +1680,19 @@ bool QRegularExpression::isValid() const QString QRegularExpression::errorString() const { d.data()->compilePattern(); - if (d->errorString) - return QCoreApplication::translate("QRegularExpression", d->errorString); + if (d->errorCode) { + QString errorString; + int errorStringLength; + do { + errorString.resize(errorString.length() + 64); + errorStringLength = pcre2_get_error_message_16(d->errorCode, + reinterpret_cast<ushort *>(errorString.data()), + errorString.length()); + } while (errorStringLength < 0); + errorString.resize(errorStringLength); + + return QCoreApplication::translate("QRegularExpression", std::move(errorString).toLatin1().constData()); + } return QCoreApplication::translate("QRegularExpression", "no error"); } @@ -2583,7 +2594,8 @@ QDebug operator<<(QDebug debug, const QRegularExpressionMatch &match) and semantics are as close as possible to those of the Perl 5 language. Written by Philip Hazel - Copyright (c) 1997-2012 University of Cambridge + Original API code Copyright (c) 1997-2012 University of Cambridge + New API code Copyright (c) 2015 University of Cambridge ----------------------------------------------------------------------------- Redistribution and use in source and binary forms, with or without @@ -2625,80 +2637,149 @@ static const char *pcreCompileErrorCodes[] = QT_TRANSLATE_NOOP("QRegularExpression", "missing terminating ] for character class"), QT_TRANSLATE_NOOP("QRegularExpression", "invalid escape sequence in character class"), QT_TRANSLATE_NOOP("QRegularExpression", "range out of order in character class"), - QT_TRANSLATE_NOOP("QRegularExpression", "nothing to repeat"), + QT_TRANSLATE_NOOP("QRegularExpression", "quantifier does not follow a repeatable item"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unexpected repeat"), QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (? or (?-"), QT_TRANSLATE_NOOP("QRegularExpression", "POSIX named classes are supported only within a class"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing )"), + QT_TRANSLATE_NOOP("QRegularExpression", "POSIX collating elements are not supported"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing closing parenthesis"), QT_TRANSLATE_NOOP("QRegularExpression", "reference to non-existent subpattern"), - QT_TRANSLATE_NOOP("QRegularExpression", "erroffset passed as NULL"), - QT_TRANSLATE_NOOP("QRegularExpression", "unknown option bit(s) set"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing ) after comment"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern passed as NULL"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognised compile-time option bit(s)"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing ) after (?# comment"), + QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested"), QT_TRANSLATE_NOOP("QRegularExpression", "regular expression is too large"), - QT_TRANSLATE_NOOP("QRegularExpression", "failed to get memory"), - QT_TRANSLATE_NOOP("QRegularExpression", "unmatched parentheses"), + QT_TRANSLATE_NOOP("QRegularExpression", "failed to allocate heap memory"), + QT_TRANSLATE_NOOP("QRegularExpression", "unmatched closing parenthesis"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: code overflow"), - QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?<"), + QT_TRANSLATE_NOOP("QRegularExpression", "letter or underscore expected after (?< or (?'"), QT_TRANSLATE_NOOP("QRegularExpression", "lookbehind assertion is not fixed length"), QT_TRANSLATE_NOOP("QRegularExpression", "malformed number or name after (?("), QT_TRANSLATE_NOOP("QRegularExpression", "conditional group contains more than two branches"), - QT_TRANSLATE_NOOP("QRegularExpression", "assertion expected after (?("), + QT_TRANSLATE_NOOP("QRegularExpression", "assertion expected after (?( or (?(?C)"), QT_TRANSLATE_NOOP("QRegularExpression", "(?R or (?[+-]digits must be followed by )"), QT_TRANSLATE_NOOP("QRegularExpression", "unknown POSIX class name"), - QT_TRANSLATE_NOOP("QRegularExpression", "POSIX collating elements are not supported"), - QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UTF8 support"), - QT_TRANSLATE_NOOP("QRegularExpression", "character value in \\x{...} sequence is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error in pcre2_study(): should not occur"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE2 does not have Unicode support"), + QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested (stack check)"), + QT_TRANSLATE_NOOP("QRegularExpression", "character code point value in \\x{} or \\o{} is too large"), QT_TRANSLATE_NOOP("QRegularExpression", "invalid condition (?(0)"), - QT_TRANSLATE_NOOP("QRegularExpression", "\\C not allowed in lookbehind assertion"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\C is not allowed in a lookbehind assertion"), QT_TRANSLATE_NOOP("QRegularExpression", "PCRE does not support \\L, \\l, \\N{name}, \\U, or \\u"), - QT_TRANSLATE_NOOP("QRegularExpression", "number after (?C is > 255"), - QT_TRANSLATE_NOOP("QRegularExpression", "closing ) for (?C expected"), - QT_TRANSLATE_NOOP("QRegularExpression", "recursive call could loop indefinitely"), + QT_TRANSLATE_NOOP("QRegularExpression", "number after (?C is greater than 255"), + QT_TRANSLATE_NOOP("QRegularExpression", "closing parenthesis for (?C expected"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid escape sequence in (*VERB) name"), QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized character after (?P"), QT_TRANSLATE_NOOP("QRegularExpression", "syntax error in subpattern name (missing terminator)"), - QT_TRANSLATE_NOOP("QRegularExpression", "two named subpatterns have the same name"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-8 string"), - QT_TRANSLATE_NOOP("QRegularExpression", "support for \\P, \\p, and \\X has not been compiled"), + QT_TRANSLATE_NOOP("QRegularExpression", "two named subpatterns have the same name (PCRE2_DUPNAMES not set)"), + QT_TRANSLATE_NOOP("QRegularExpression", "group name must start with a non-digit"), + QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE2 does not have support for \\P, \\p, or \\X"), QT_TRANSLATE_NOOP("QRegularExpression", "malformed \\P or \\p sequence"), QT_TRANSLATE_NOOP("QRegularExpression", "unknown property name after \\P or \\p"), - QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name is too long (maximum 32 characters)"), - QT_TRANSLATE_NOOP("QRegularExpression", "too many named subpatterns (maximum 10000)"), - QT_TRANSLATE_NOOP("QRegularExpression", "octal value is greater than \\377 (not in UTF-8 mode)"), + QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name is too long (maximum " "10000" " characters)"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many named subpatterns (maximum " "256" ")"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid range in character class"), + QT_TRANSLATE_NOOP("QRegularExpression", "octal value is greater than \\377 in 8-bit non-UTF-8 mode"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: overran compiling workspace"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: previously-checked referenced subpattern not found"), QT_TRANSLATE_NOOP("QRegularExpression", "DEFINE group contains more than one branch"), - QT_TRANSLATE_NOOP("QRegularExpression", "repeating a DEFINE group is not allowed"), - QT_TRANSLATE_NOOP("QRegularExpression", "inconsistent NEWLINE options"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing opening brace after \\o"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown newline setting"), QT_TRANSLATE_NOOP("QRegularExpression", "\\g is not followed by a braced, angle-bracketed, or quoted name/number or by a plain number"), QT_TRANSLATE_NOOP("QRegularExpression", "a numbered reference must not be zero"), QT_TRANSLATE_NOOP("QRegularExpression", "an argument is not allowed for (*ACCEPT), (*FAIL), or (*COMMIT)"), - QT_TRANSLATE_NOOP("QRegularExpression", "(*VERB) not recognized"), + QT_TRANSLATE_NOOP("QRegularExpression", "(*VERB) not recognized or malformed"), QT_TRANSLATE_NOOP("QRegularExpression", "number is too big"), QT_TRANSLATE_NOOP("QRegularExpression", "subpattern name expected"), QT_TRANSLATE_NOOP("QRegularExpression", "digit expected after (?+"), - QT_TRANSLATE_NOOP("QRegularExpression", "] is an invalid data character in JavaScript compatibility mode"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-octal character in \\o{} (closing brace missing?)"), QT_TRANSLATE_NOOP("QRegularExpression", "different names for subpatterns of the same number are not allowed"), QT_TRANSLATE_NOOP("QRegularExpression", "(*MARK) must have an argument"), - QT_TRANSLATE_NOOP("QRegularExpression", "this version of PCRE is not compiled with PCRE_UCP support"), - QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by an ASCII character"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-hex character in \\x{} (closing brace missing?)"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by a printable ASCII character"), + QT_TRANSLATE_NOOP("QRegularExpression", "\\c must be followed by a letter or one of [\\]^_?"), QT_TRANSLATE_NOOP("QRegularExpression", "\\k is not followed by a braced, angle-bracketed, or quoted name"), QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown opcode in find_fixedlength()"), QT_TRANSLATE_NOOP("QRegularExpression", "\\N is not supported in a class"), - QT_TRANSLATE_NOOP("QRegularExpression", "too many forward references"), + QT_TRANSLATE_NOOP("QRegularExpression", "SPARE ERROR"), QT_TRANSLATE_NOOP("QRegularExpression", "disallowed Unicode code point (>= 0xd800 && <= 0xdfff)"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-16 string"), + QT_TRANSLATE_NOOP("QRegularExpression", "using UTF is disabled by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "using UCP is disabled by the application"), QT_TRANSLATE_NOOP("QRegularExpression", "name is too long in (*MARK), (*PRUNE), (*SKIP), or (*THEN)"), - QT_TRANSLATE_NOOP("QRegularExpression", "character value in \\u.... sequence is too large"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid UTF-32 string"), - QT_TRANSLATE_NOOP("QRegularExpression", "setting UTF is disabled by the application"), - QT_TRANSLATE_NOOP("QRegularExpression", "non-hex character in \\x{} (closing brace missing?)"), - QT_TRANSLATE_NOOP("QRegularExpression", "non-octal character in \\o{} (closing brace missing?)"), - QT_TRANSLATE_NOOP("QRegularExpression", "missing opening brace after \\o"), - QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested"), - QT_TRANSLATE_NOOP("QRegularExpression", "invalid range in character class"), - QT_TRANSLATE_NOOP("QRegularExpression", "group name must start with a non-digit"), - QT_TRANSLATE_NOOP("QRegularExpression", "parentheses are too deeply nested (stack check)"), - QT_TRANSLATE_NOOP("QRegularExpression", "digits missing in \\x{} or \\o{}") + QT_TRANSLATE_NOOP("QRegularExpression", "character code point value in \\u.... sequence is too large"), + QT_TRANSLATE_NOOP("QRegularExpression", "digits missing in \\x{} or \\o{}"), + QT_TRANSLATE_NOOP("QRegularExpression", "syntax error in (?(VERSION condition"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error: unknown opcode in auto_possessify()"), + QT_TRANSLATE_NOOP("QRegularExpression", "missing terminating delimiter for callout with string argument"), + QT_TRANSLATE_NOOP("QRegularExpression", "unrecognized string delimiter follows (?C"), + QT_TRANSLATE_NOOP("QRegularExpression", "using \\C is disabled by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "(?| and/or (?J: or (?x: parentheses are too deeply nested"), + QT_TRANSLATE_NOOP("QRegularExpression", "using \\C is disabled in this PCRE2 library"), + QT_TRANSLATE_NOOP("QRegularExpression", "regular expression is too complicated"), + QT_TRANSLATE_NOOP("QRegularExpression", "lookbehind assertion is too long"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern string is longer than the limit set by the application"), + QT_TRANSLATE_NOOP("QRegularExpression", "no error"), + QT_TRANSLATE_NOOP("QRegularExpression", "no match"), + QT_TRANSLATE_NOOP("QRegularExpression", "partial match"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 1 byte missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 2 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 3 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 4 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 5 bytes missing at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 2 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 3 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 4 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 5 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: byte 6 top bits not 0x80"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 5-byte character is not allowed (RFC 3629)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: 6-byte character is not allowed (RFC 3629)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: code points greater than 0x10ffff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: code points 0xd800-0xdfff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 2-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 3-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 4-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 5-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: overlong 6-byte sequence"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: isolated byte with 0x80 bit set"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-8 error: illegal byte (0xfe or 0xff)"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: missing low surrogate at end"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: invalid low surrogate"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-16 error: isolated low surrogate"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-32 error: code points 0xd800-0xdfff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "UTF-32 error: code points greater than 0x10ffff are not defined"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad data value"), + QT_TRANSLATE_NOOP("QRegularExpression", "patterns do not all use the same character tables"), + QT_TRANSLATE_NOOP("QRegularExpression", "magic number missing"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern compiled in wrong mode: 8/16/32-bit error"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad offset value"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad option value"), + QT_TRANSLATE_NOOP("QRegularExpression", "invalid replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad offset into UTF string"), + QT_TRANSLATE_NOOP("QRegularExpression", "callout error code"), /* Never returned by PCRE2 itself */ + QT_TRANSLATE_NOOP("QRegularExpression", "invalid data in workspace for DFA restart"), + QT_TRANSLATE_NOOP("QRegularExpression", "too much recursion for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "backreference condition or recursion test is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "function is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "pattern contains an item that is not supported for DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "workspace size exceeded in DFA matching"), + QT_TRANSLATE_NOOP("QRegularExpression", "internal error - pattern overwritten?"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad JIT option"), + QT_TRANSLATE_NOOP("QRegularExpression", "JIT stack limit reached"), + QT_TRANSLATE_NOOP("QRegularExpression", "match limit exceeded"), + QT_TRANSLATE_NOOP("QRegularExpression", "no more memory"), + QT_TRANSLATE_NOOP("QRegularExpression", "unknown substring"), + QT_TRANSLATE_NOOP("QRegularExpression", "non-unique substring name"), + QT_TRANSLATE_NOOP("QRegularExpression", "NULL argument passed"), + QT_TRANSLATE_NOOP("QRegularExpression", "nested recursion at the same subject position"), + QT_TRANSLATE_NOOP("QRegularExpression", "recursion limit exceeded"), + QT_TRANSLATE_NOOP("QRegularExpression", "requested value is not available"), + QT_TRANSLATE_NOOP("QRegularExpression", "requested value is not set"), + QT_TRANSLATE_NOOP("QRegularExpression", "offset limit set without PCRE2_USE_OFFSET_LIMIT"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad escape sequence in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "expected closing curly bracket in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "bad substitution in replacement string"), + QT_TRANSLATE_NOOP("QRegularExpression", "match with end before start is not supported"), + QT_TRANSLATE_NOOP("QRegularExpression", "too many replacements (more than INT_MAX)") }; #endif // #if 0 diff --git a/src/corelib/tools/qringbuffer.cpp b/src/corelib/tools/qringbuffer.cpp index cb11e72435..8fa378e935 100644 --- a/src/corelib/tools/qringbuffer.cpp +++ b/src/corelib/tools/qringbuffer.cpp @@ -132,7 +132,6 @@ char *QRingBuffer::reserve(qint64 bytes) char *writePtr = buffers.last().data() + tail; bufferSize += bytes; - Q_ASSERT(bytes < MaxByteArraySize); tail += int(bytes); return writePtr; } diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 5738413bfb..0d42c8a212 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -974,13 +974,13 @@ qobject_cast(const QWeakPointer<T> &src) } template<typename T> -QWeakPointer<typename QtPrivate::QEnableIf<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::Type> +QWeakPointer<typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::type> qWeakPointerFromVariant(const QVariant &variant) { return QWeakPointer<T>(qobject_cast<T*>(QtSharedPointer::weakPointerFromVariant_internal(variant).data())); } template<typename T> -QSharedPointer<typename QtPrivate::QEnableIf<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::Type> +QSharedPointer<typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::type> qSharedPointerFromVariant(const QVariant &variant) { return qSharedPointerObjectCast<T>(QtSharedPointer::sharedPointerFromVariant_internal(variant)); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 247119d178..48f3d64c4a 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -211,7 +211,8 @@ void qt_from_latin1(ushort *dst, const char *str, size_t size) Q_DECL_NOTHROW { /* SIMD: * Unpacking with SSE has been shown to improve performance on recent CPUs - * The same method gives no improvement with NEON. + * The same method gives no improvement with NEON. On Aarch64, clang will do the vectorization + * itself in exactly the same way as one would do it with intrinsics. */ #if defined(__SSE2__) const char *e = str + size; @@ -491,6 +492,30 @@ static int ucstrncmp(const QChar *a, const QChar *b, int l) return UnrollTailLoop<7>::exec(l, 0, lambda, lambda); # endif #endif +#if defined(__ARM_NEON__) && defined(Q_PROCESSOR_ARM_64) // vaddv is only available on Aarch64 + if (l >= 8) { + const QChar *end = a + l; + const uint16x8_t mask = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 }; + while (a + 7 < end) { + uint16x8_t da = vld1q_u16(reinterpret_cast<const uint16_t *>(a)); + uint16x8_t db = vld1q_u16(reinterpret_cast<const uint16_t *>(b)); + + uint8_t r = ~(uint8_t)vaddvq_u16(vandq_u16(vceqq_u16(da, db), mask)); + if (r) { + // found a different QChar + uint idx = qCountTrailingZeroBits(r); + return (int)a[idx].unicode() - (int)b[idx].unicode(); + } + a += 8; + b += 8; + } + l &= 7; + } + const auto &lambda = [=](int i) -> int { + return a[i].unicode() - b[i].unicode(); + }; + return UnrollTailLoop<7>::exec(l, 0, lambda, lambda); +#endif // __ARM_NEON__ if (!l) return 0; @@ -703,6 +728,18 @@ static int findChar(const QChar *str, int len, QChar ch, int from, [=](int i) { return n - s + i; }); # endif #endif +#if defined(__ARM_NEON__) && defined(Q_PROCESSOR_ARM_64) // vaddv is only available on Aarch64 + const uint16x8_t vmask = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 }; + const uint16x8_t ch_vec = vdupq_n_u16(c); + for (const ushort *next = n + 8; next <= e; n = next, next += 8) { + uint16x8_t data = vld1q_u16(n); + uint mask = vaddvq_u16(vandq_u16(vceqq_u16(data, ch_vec), vmask)); + if (ushort(mask)) { + // found a match + return n - s + qCountTrailingZeroBits(mask); + } + } +#endif // aarch64 --n; while (++n != e) if (*n == c) @@ -742,7 +779,9 @@ inline char qToLower(char ch) } +#if QT_DEPRECATED_SINCE(5, 9) const QString::Null QString::null = { }; +#endif /*! \macro QT_RESTRICTED_CAST_FROM_ASCII @@ -1617,7 +1656,7 @@ QString::QString(QChar ch) \internal */ -/*! \fn QString &QString::operator=(const Null &) +/*! \fn QString &QString::operator=(const QString::Null &) \internal */ @@ -1767,17 +1806,11 @@ void QString::resize(int size, QChar fillChar) void QString::reallocData(uint alloc, bool grow) { - size_t blockSize; - if (grow) { - auto r = qCalculateGrowingBlockSize(alloc, sizeof(QChar), sizeof(Data)); - blockSize = r.size; - alloc = uint(r.elementCount); - } else { - blockSize = qCalculateBlockSize(alloc, sizeof(QChar), sizeof(Data)); - } + auto allocOptions = d->detachFlags(); + if (grow) + allocOptions |= QArrayData::Grow; if (d->ref.isShared() || IS_RAW_DATA(d)) { - Data::AllocationOptions allocOptions(d->capacityReserved ? Data::CapacityReserved : 0); Data *x = Data::allocate(alloc, allocOptions); Q_CHECK_PTR(x); x->size = qMin(int(alloc) - 1, d->size); @@ -1787,11 +1820,9 @@ void QString::reallocData(uint alloc, bool grow) Data::deallocate(d); d = x; } else { - Data *p = static_cast<Data *>(::realloc(d, blockSize)); + Data *p = Data::reallocateUnaligned(d, alloc, allocOptions); Q_CHECK_PTR(p); d = p; - d->alloc = alloc; - d->offset = sizeof(QStringData); } } @@ -4466,11 +4497,11 @@ bool QString::startsWith(const QStringRef &s, Qt::CaseSensitivity cs) const \sa startsWith() */ -bool QString::endsWith(const QString& s, Qt::CaseSensitivity cs) const +bool QString::endsWith(const QString &s, Qt::CaseSensitivity cs) const { return qt_ends_with(isNull() ? 0 : unicode(), size(), s.isNull() ? 0 : s.unicode(), s.size(), cs); - } +} /*! \since 4.8 @@ -4606,6 +4637,8 @@ QByteArray QString::toLatin1_helper_inplace(QString &s) QByteArray QString::toLocal8Bit_helper(const QChar *data, int size) { + if (!data) + return QByteArray(); #ifndef QT_NO_TEXTCODEC QTextCodec *localeCodec = QTextCodec::codecForLocale(); if (localeCodec) @@ -4887,6 +4920,7 @@ QString QString::fromUcs4(const uint *unicode, int size) return QUtf32::convertToUnicode((const char *)unicode, size*4, 0); } + /*! Resizes the string to \a size characters and copies \a unicode into the string. @@ -5981,7 +6015,10 @@ static uint parse_flag_characters(const char * &c) Q_DECL_NOTHROW uint flags = QLocaleData::ZeroPadExponent; while (true) { switch (*c) { - case '#': flags |= QLocaleData::Alternate; break; + case '#': + flags |= QLocaleData::ShowBase | QLocaleData::AddTrailingZeroes + | QLocaleData::ForcePoint; + break; case '0': flags |= QLocaleData::ZeroPadded; break; case '-': flags |= QLocaleData::LeftAdjusted; break; case ' ': flags |= QLocaleData::BlankBeforePositive; break; @@ -6239,7 +6276,7 @@ QString QString::vasprintf(const char *cformat, va_list ap) case 'p': { void *arg = va_arg(ap, void*); const quint64 i = reinterpret_cast<quintptr>(arg); - flags |= QLocaleData::Alternate; + flags |= QLocaleData::ShowBase; subst = QLocaleData::c()->unsLongLongToString(i, precision, 16, width, flags); ++c; break; @@ -7814,10 +7851,13 @@ QString QString::arg(double a, int fieldWidth, char fmt, int prec, QChar fillCha if (d.locale_occurrences > 0) { QLocale locale; - if (!(locale.numberOptions() & QLocale::OmitGroupSeparator)) + const QLocale::NumberOptions numberOptions = locale.numberOptions(); + if (!(numberOptions & QLocale::OmitGroupSeparator)) flags |= QLocaleData::ThousandsGroup; - if (!(locale.numberOptions() & QLocale::OmitLeadingZeroInExponent)) + if (!(numberOptions & QLocale::OmitLeadingZeroInExponent)) flags |= QLocaleData::ZeroPadExponent; + if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot) + flags |= QLocaleData::AddTrailingZeroes; locale_arg = locale.d->m_data->doubleToString(a, prec, form, fieldWidth, flags); } @@ -8025,33 +8065,12 @@ bool QString::isSimpleText() const /*! \fn bool QString::isRightToLeft() const Returns \c true if the string is read right to left. + + \sa QStringRef::isRightToLeft() */ bool QString::isRightToLeft() const { - const ushort *p = d->data(); - const ushort * const end = p + d->size; - while (p < end) { - uint ucs4 = *p; - if (QChar::isHighSurrogate(ucs4) && p < end - 1) { - ushort low = p[1]; - if (QChar::isLowSurrogate(low)) { - ucs4 = QChar::surrogateToUcs4(ucs4, low); - ++p; - } - } - switch (QChar::direction(ucs4)) - { - case QChar::DirL: - return false; - case QChar::DirR: - case QChar::DirAL: - return true; - default: - break; - } - ++p; - } - return false; + return QStringRef(this).isRightToLeft(); } /*! \fn QChar *QString::data() @@ -9007,7 +9026,7 @@ ownership of it, no memory is freed when instances are destroyed. Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first character in the string. - \sa cbegin(), end(), rbegin(), rend() + \sa cbegin(), constBegin(), end(), constEnd(), rbegin(), rend() */ /*! @@ -9016,7 +9035,16 @@ ownership of it, no memory is freed when instances are destroyed. Same as begin(). - \sa begin(), cend(), rbegin(), rend() + \sa begin(), constBegin(), cend(), constEnd(), rbegin(), rend() +*/ + +/*! + \fn QStringRef::const_iterator QStringRef::constBegin() const + \since 5.9 + + Same as begin(). + + \sa begin(), cend(), constEnd(), rbegin(), rend() */ /*! @@ -9026,7 +9054,7 @@ ownership of it, no memory is freed when instances are destroyed. Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary character after the last character in the list. - \sa cbegin(), end(), rbegin(), rend() + \sa cbegin(), constBegin(), end(), constEnd(), rbegin(), rend() */ /*! \fn QStringRef::const_iterator QStringRef::cend() const @@ -9034,7 +9062,15 @@ ownership of it, no memory is freed when instances are destroyed. Same as end(). - \sa end(), cbegin(), rbegin(), rend() + \sa end(), constEnd(), cbegin(), constBegin(), rbegin(), rend() +*/ + +/*! \fn QStringRef::const_iterator QStringRef::constEnd() const + \since 5.9 + + Same as end(). + + \sa end(), cend(), cbegin(), constBegin(), rbegin(), rend() */ /*! @@ -9585,9 +9621,7 @@ QStringRef QStringRef::left(int n) const */ QStringRef QString::leftRef(int n) const { - if (uint(n) >= uint(d->size)) - n = d->size; - return QStringRef(this, 0, n); + return QStringRef(this).left(n); } /*! @@ -9624,9 +9658,7 @@ QStringRef QStringRef::right(int n) const */ QStringRef QString::rightRef(int n) const { - if (uint(n) >= uint(d->size)) - n = d->size; - return QStringRef(this, d->size - n, n); + return QStringRef(this).right(n); } /*! @@ -9685,19 +9717,7 @@ QStringRef QStringRef::mid(int pos, int n) const */ QStringRef QString::midRef(int position, int n) const { - using namespace QtPrivate; - switch (QContainerImplHelper::mid(d->size, &position, &n)) { - case QContainerImplHelper::Null: - return QStringRef(); - case QContainerImplHelper::Empty: - return QStringRef(this, 0, 0); - case QContainerImplHelper::Full: - return QStringRef(this, 0, d->size); - case QContainerImplHelper::Subset: - return QStringRef(this, position, n); - } - Q_UNREACHABLE(); - return QStringRef(); + return QStringRef(this).mid(position, n); } /*! @@ -9944,6 +9964,41 @@ int QStringRef::count(const QStringRef &str, Qt::CaseSensitivity cs) const } /*! + \since 5.9 + + Returns \c true if the string is read right to left. + + \sa QString::isRightToLeft() +*/ +bool QStringRef::isRightToLeft() const +{ + const ushort *p = reinterpret_cast<const ushort*>(unicode()); + const ushort * const end = p + size(); + while (p < end) { + uint ucs4 = *p; + if (QChar::isHighSurrogate(ucs4) && p < end - 1) { + ushort low = p[1]; + if (QChar::isLowSurrogate(low)) { + ucs4 = QChar::surrogateToUcs4(ucs4, low); + ++p; + } + } + switch (QChar::direction(ucs4)) + { + case QChar::DirL: + return false; + case QChar::DirR: + case QChar::DirAL: + return true; + default: + break; + } + ++p; + } + return false; +} + +/*! \since 4.8 Returns \c true if the string reference starts with \a str; otherwise @@ -10309,6 +10364,8 @@ static inline bool qt_ends_with(const QChar *haystack, int haystackLen, */ QByteArray QStringRef::toLatin1() const { + if (isNull()) + return QByteArray(); return QString::toLatin1_helper(unicode(), length()); } @@ -10347,9 +10404,11 @@ QByteArray QStringRef::toLatin1() const QByteArray QStringRef::toLocal8Bit() const { #ifndef QT_NO_TEXTCODEC - QTextCodec *localeCodec = QTextCodec::codecForLocale(); - if (localeCodec) - return localeCodec->fromUnicode(unicode(), length()); + if (!isNull()) { + QTextCodec *localeCodec = QTextCodec::codecForLocale(); + if (localeCodec) + return localeCodec->fromUnicode(unicode(), length()); + } #endif // QT_NO_TEXTCODEC return toLatin1(); } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index b50b2ee4e5..1bd436c387 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -144,15 +144,10 @@ typedef QLatin1String QLatin1Literal; typedef QTypedArrayData<ushort> QStringData; -#if defined(Q_COMPILER_UNICODE_STRINGS) - -#define QT_UNICODE_LITERAL_II(str) u"" str -typedef char16_t qunicodechar; - -#elif defined(Q_OS_WIN) \ - || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) \ - || (!defined(__SIZEOF_WCHAR_T__) && defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536)) -// wchar_t is 2 bytes +#if defined(Q_OS_WIN) && !defined(Q_COMPILER_UNICODE_STRINGS) +// fall back to wchar_t if the a Windows compiler does not +// support Unicode string literals, assuming wchar_t is 2 bytes +// on that platform (sanity-checked by static_assert further below) #if defined(Q_CC_MSVC) # define QT_UNICODE_LITERAL_II(str) L##str @@ -162,21 +157,22 @@ typedef char16_t qunicodechar; typedef wchar_t qunicodechar; #else +// all our supported compilers support Unicode string literals, +// even if their Q_COMPILER_UNICODE_STRING has been revoked due +// to lacking stdlib support. But QStringLiteral only needs the +// core language feature, so just use u"" here unconditionally: -#define QT_NO_UNICODE_LITERAL -typedef ushort qunicodechar; +#define QT_UNICODE_LITERAL_II(str) u"" str +typedef char16_t qunicodechar; #endif Q_STATIC_ASSERT_X(sizeof(qunicodechar) == 2, "qunicodechar must typedef an integral type of size 2"); -#ifndef QT_NO_UNICODE_LITERAL -# define QT_UNICODE_LITERAL(str) QT_UNICODE_LITERAL_II(str) -# if defined(Q_COMPILER_LAMBDA) - -# define QStringLiteral(str) \ - ([]() -> QString { \ +#define QT_UNICODE_LITERAL(str) QT_UNICODE_LITERAL_II(str) +#define QStringLiteral(str) \ + ([]() Q_DECL_NOEXCEPT -> QString { \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ static const QStaticStringData<Size> qstring_literal = { \ Q_STATIC_STRING_DATA_HEADER_INITIALIZER(Size), \ @@ -187,17 +183,6 @@ Q_STATIC_ASSERT_X(sizeof(qunicodechar) == 2, }()) \ /**/ -# endif -#endif // QT_NO_UNICODE_LITERAL - -#ifndef QStringLiteral -// no lambdas, not GCC, or GCC in C++98 mode with 4-byte wchar_t -// fallback, return a temporary QString -// source code is assumed to be encoded in UTF-8 - -# define QStringLiteral(str) QString::fromUtf8("" str "", sizeof(str) - 1) -#endif - #define Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \ { Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, offset } \ /**/ @@ -397,7 +382,7 @@ public: 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 Q_REQUIRED_RESULT; -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) && !defined(Q_CLANG_QDOC) # if defined(Q_CC_GNU) // required due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61941 # pragma push_macro("Q_REQUIRED_RESULT") @@ -522,7 +507,7 @@ public: const ushort *utf16() const; -#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(QT_COMPILING_QSTRING_COMPAT_CPP) && !defined(Q_CLANG_QDOC) QByteArray toLatin1() const & Q_REQUIRED_RESULT { return toLatin1_helper(*this); } QByteArray toLatin1() && Q_REQUIRED_RESULT @@ -532,9 +517,9 @@ public: QByteArray toUtf8() && Q_REQUIRED_RESULT { return toUtf8_helper(*this); } QByteArray toLocal8Bit() const & Q_REQUIRED_RESULT - { return toLocal8Bit_helper(constData(), size()); } + { return toLocal8Bit_helper(isNull() ? nullptr : constData(), size()); } QByteArray toLocal8Bit() && Q_REQUIRED_RESULT - { return toLocal8Bit_helper(constData(), size()); } + { return toLocal8Bit_helper(isNull() ? nullptr : constData(), size()); } #else QByteArray toLatin1() const Q_REQUIRED_RESULT; QByteArray toUtf8() const Q_REQUIRED_RESULT; @@ -785,10 +770,13 @@ public: NSString *toNSString() const Q_DECL_NS_RETURNS_AUTORELEASED; #endif // compatibility +#if QT_DEPRECATED_SINCE(5, 9) struct Null { }; + QT_DEPRECATED_X("use QString()") static const Null null; inline QString(const Null &): d(Data::sharedNull()) {} inline QString &operator=(const Null &) { *this = QString(); return *this; } +#endif inline bool isNull() const { return d == Data::sharedNull(); } @@ -1149,13 +1137,18 @@ inline bool QString::contains(QLatin1String s, Qt::CaseSensitivity cs) const inline bool QString::contains(QChar c, Qt::CaseSensitivity cs) const { return indexOf(c, 0, cs) != -1; } - +#if QT_DEPRECATED_SINCE(5, 9) inline bool operator==(QString::Null, QString::Null) { return true; } +QT_DEPRECATED_X("use QString::isNull()") inline bool operator==(QString::Null, const QString &s) { return s.isNull(); } +QT_DEPRECATED_X("use QString::isNull()") inline bool operator==(const QString &s, QString::Null) { return s.isNull(); } inline bool operator!=(QString::Null, QString::Null) { return false; } +QT_DEPRECATED_X("use !QString::isNull()") inline bool operator!=(QString::Null, const QString &s) { return !s.isNull(); } +QT_DEPRECATED_X("use !QString::isNull()") inline bool operator!=(const QString &s, QString::Null) { return !s.isNull(); } +#endif inline bool operator==(QLatin1String s1, QLatin1String s2) Q_DECL_NOTHROW { return s1.size() == s2.size() && (!s1.size() || !memcmp(s1.latin1(), s2.latin1(), s1.size())); } @@ -1400,7 +1393,8 @@ public: QStringRef(QStringRef &&other) Q_DECL_NOTHROW : m_string(other.m_string), m_position(other.m_position), m_size(other.m_size) {} QStringRef &operator=(QStringRef &&other) Q_DECL_NOTHROW { return *this = other; } #endif - QStringRef &operator=(const QStringRef &other) Q_DECL_NOTHROW { + QStringRef &operator=(const QStringRef &other) Q_DECL_NOTHROW + { m_string = other.m_string; m_position = other.m_position; m_size = other.m_size; return *this; } @@ -1449,6 +1443,8 @@ public: m_size -= n; } + bool isRightToLeft() const; + bool startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(QLatin1String s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; bool startsWith(QChar c, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; @@ -1461,7 +1457,8 @@ public: inline QStringRef &operator=(const QString *string); - inline const QChar *unicode() const { + inline const QChar *unicode() const + { if (!m_string) return reinterpret_cast<const QChar *>(QString::Data::sharedNull()->data()); return m_string->unicode() + m_position; @@ -1471,8 +1468,10 @@ public: inline const_iterator begin() const { return unicode(); } inline const_iterator cbegin() const { return unicode(); } + inline const_iterator constBegin() const { return unicode(); } inline const_iterator end() const { return unicode() + size(); } inline const_iterator cend() const { return unicode() + size(); } + inline const_iterator constEnd() const { return unicode() + size(); } inline const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } inline const_reverse_iterator crbegin() const { return rbegin(); } inline const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } diff --git a/src/corelib/tools/qstring_compat.cpp b/src/corelib/tools/qstring_compat.cpp index 300f8467b1..45bb816e4b 100644 --- a/src/corelib/tools/qstring_compat.cpp +++ b/src/corelib/tools/qstring_compat.cpp @@ -80,7 +80,7 @@ QByteArray QString::toLatin1() const QByteArray QString::toLocal8Bit() const { - return toLocal8Bit_helper(constData(), size()); + return toLocal8Bit_helper(isNull() ? nullptr : constData(), size()); } QByteArray QString::toUtf8() const diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index b2832b5fbe..9a40abcfed 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -277,9 +277,9 @@ template <> struct QConcatenable<QStringRef> : private QAbstractConcatenable } }; -template <int N> struct QConcatenable<char[N]> : private QAbstractConcatenable +template <int N> struct QConcatenable<const char[N]> : private QAbstractConcatenable { - typedef char type[N]; + typedef const char type[N]; typedef QByteArray ConvertTo; enum { ExactSize = false }; static int size(const char[N]) { return N - 1; } @@ -296,23 +296,9 @@ template <int N> struct QConcatenable<char[N]> : private QAbstractConcatenable } }; -template <int N> struct QConcatenable<const char[N]> : private QAbstractConcatenable +template <int N> struct QConcatenable<char[N]> : QConcatenable<const char[N]> { - typedef const char type[N]; - typedef QByteArray ConvertTo; - enum { ExactSize = false }; - static int size(const char[N]) { return N - 1; } -#ifndef QT_NO_CAST_FROM_ASCII - static inline void QT_ASCII_CAST_WARN appendTo(const char a[N], QChar *&out) - { - QAbstractConcatenable::convertFromAscii(a, N - 1, out); - } -#endif - static inline void appendTo(const char a[N], char *&out) - { - while (*a) - *out++ = *a++; - } + typedef char type[N]; }; template <> struct QConcatenable<const char *> : private QAbstractConcatenable diff --git a/src/corelib/tools/qtimezone.h b/src/corelib/tools/qtimezone.h index 083f87f39f..bd87139f5b 100644 --- a/src/corelib/tools/qtimezone.h +++ b/src/corelib/tools/qtimezone.h @@ -47,6 +47,11 @@ QT_REQUIRE_CONFIG(timezone); +#if (defined(Q_OS_DARWIN) || defined(Q_QDOC)) && !defined(QT_NO_SYSTEMLOCALE) +Q_FORWARD_DECLARE_CF_TYPE(CFTimeZone); +Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone); +#endif + QT_BEGIN_NAMESPACE class QTimeZonePrivate; @@ -142,6 +147,13 @@ public: static QList<QByteArray> windowsIdToIanaIds(const QByteArray &windowsId, QLocale::Country country); +#if (defined(Q_OS_DARWIN) || defined(Q_QDOC)) && !defined(QT_NO_SYSTEMLOCALE) + static QTimeZone fromCFTimeZone(CFTimeZoneRef timeZone); + CFTimeZoneRef toCFTimeZone() const Q_DECL_CF_RETURNS_RETAINED; + static QTimeZone fromNSTimeZone(const NSTimeZone *timeZone); + NSTimeZone *toNSTimeZone() const Q_DECL_NS_RETURNS_AUTORELEASED; +#endif + private: QTimeZone(QTimeZonePrivate &dd); #ifndef QT_NO_DATASTREAM diff --git a/src/corelib/tools/qtimezoneprivate.cpp b/src/corelib/tools/qtimezoneprivate.cpp index 2ff03eddec..7b780ecf7d 100644 --- a/src/corelib/tools/qtimezoneprivate.cpp +++ b/src/corelib/tools/qtimezoneprivate.cpp @@ -49,10 +49,6 @@ QT_BEGIN_NAMESPACE -enum { - MSECS_TRAN_WINDOW = 21600000 // 6 hour window for possible recent transitions -}; - /* Static utilities for looking up Windows ID tables */ @@ -140,7 +136,7 @@ QTimeZonePrivate::~QTimeZonePrivate() { } -QTimeZonePrivate *QTimeZonePrivate::clone() +QTimeZonePrivate *QTimeZonePrivate::clone() const { return new QTimeZonePrivate(*this); } @@ -248,67 +244,206 @@ QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const } // Private only method for use by QDateTime to convert local msecs to epoch msecs -// TODO Could be platform optimised if needed -QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) const +QTimeZonePrivate::Data QTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs, int hint) const { - if (!hasDaylightTime() ||!hasTransitions()) { - // No DST means same offset for all local msecs - // Having DST but no transitions means we can't calculate, so use nearest - return data(forLocalMSecs - (standardTimeOffset(forLocalMSecs) * 1000)); - } + if (!hasDaylightTime()) // No DST means same offset for all local msecs + return data(forLocalMSecs - standardTimeOffset(forLocalMSecs) * 1000); - // Get the transition for the local msecs which most of the time should be the right one - // Only around the transition times might it not be the right one - Data tran = previousTransition(forLocalMSecs); - Data nextTran; - - // If the local msecs is less than the real local time of the transition - // then get the previous transition to use instead - if (forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) { - while (tran.atMSecsSinceEpoch != invalidMSecs() - && forLocalMSecs < tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)) { - nextTran = tran; - tran = previousTransition(tran.atMSecsSinceEpoch); - } - } else { - // The zone msecs is after the transition, so check it is before the next tran - // If not try use the next transition instead - nextTran = nextTransition(tran.atMSecsSinceEpoch); + /* + We need a UTC time at which to ask for the offset, in order to be able to + add that offset to forLocalMSecs, to get the UTC time we + need. Fortunately, no time-zone offset is more than 14 hours; and DST + transitions happen (much) more than thirty-two hours apart. So sampling + offset sixteen hours each side gives us information we can be sure + brackets the correct time and at most one DST transition. + */ + const qint64 sixteenHoursInMSecs(16 * 3600 * 1000); + /* + Offsets are Local - UTC, positive to the east of Greenwich, negative to + the west; DST offset always exceeds standard offset, when DST applies. + When we have offsets on either side of a transition, the lower one is + standard, the higher is DST. + + Non-DST transitions (jurisdictions changing time-zone and time-zones + changing their standard offset, typically) are described below as if they + were DST transitions (since these are more usual and familiar); the code + mostly concerns itself with offsets from UTC, described in terms of the + common case for changes in that. If there is no actual change in offset + (e.g. a DST transition cancelled by a standard offset change), this code + should handle it gracefully; without transitions, it'll see early == late + and take the easy path; with transitions, tran and nextTran get the + correct UTC time as atMSecsSinceEpoch so comparing to nextStart selects + the right one. In all other cases, the transition changes offset and the + reasoning that applies to DST applies just the same. Aside from hinting, + the only thing that looks at DST-ness at all, other than inferred from + offset changes, is the case without transition data handling an invalid + time in the gap that a transition passed over. + + The handling of hint (see below) is apt to go wrong in non-DST + transitions. There isn't really a great deal we can hope to do about that + without adding yet more unreliable complexity to the heuristics in use for + already obscure corner-cases. + */ + + /* + The hint (really a QDateTimePrivate::DaylightStatus) is > 0 if caller + thinks we're in DST, 0 if in standard. A value of -2 means never-DST, so + should have been handled above; if it slips through, it's wrong but we + should probably treat it as standard anyway (never-DST means + always-standard, after all). If the hint turns out to be wrong, fall back + on trying the other possibility: which makes it harmless to treat -1 + (meaning unknown) as standard (i.e. try standard first, then try DST). In + practice, away from a transition, the only difference hint makes is to + which candidate we try first: if the hint is wrong (or unknown and + standard fails), we'll try the other candidate and it'll work. + + For the obscure (and invalid) case where forLocalMSecs falls in a + spring-forward's missing hour, a common case is that we started with a + date/time for which the hint was valid and adjusted it naively; for that + case, we should correct the adjustment by shunting across the transition + into where hint is wrong. So half-way through the gap, arrived at from + the DST side, should be read as an hour earlier, in standard time; but, if + arrived at from the standard side, should be read as an hour later, in + DST. (This shall be wrong in some cases; for example, when a country + changes its transition dates and changing a date/time by more than six + months lands it on a transition. However, these cases are even more + obscure than those where the heuristic is good.) + */ + + if (hasTransitions()) { + /* + We have transitions. + + Each transition gives the offsets to use until the next; so we need the + most recent transition before the time forLocalMSecs describes. If it + describes a time *in* a transition, we'll need both that transition and + the one before it. So find one transition that's probably after (and not + much before, otherwise) and another that's definitely before, then work + out which one to use. When both or neither work on forLocalMSecs, use + hint to disambiguate. + */ + + // Get a transition definitely before the local MSecs; usually all we need. + // Only around the transition times might we need another. + Data tran = previousTransition(forLocalMSecs - sixteenHoursInMSecs); + Q_ASSERT(forLocalMSecs < 0 || // Pre-epoch TZ info may be unavailable + forLocalMSecs >= tran.atMSecsSinceEpoch + tran.offsetFromUtc * 1000); + Data nextTran = nextTransition(tran.atMSecsSinceEpoch); + /* + Now walk those forward until they bracket forLocalMSecs with transitions. + + One of the transitions should then be telling us the right offset to use. + In a transition, we need the transition before it (to describe the run-up + to the transition) and the transition itself; so we need to stop when + nextTran is that transition. + */ while (nextTran.atMSecsSinceEpoch != invalidMSecs() - && forLocalMSecs >= nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000)) { + && forLocalMSecs > nextTran.atMSecsSinceEpoch + nextTran.offsetFromUtc * 1000) { + Data newTran = nextTransition(nextTran.atMSecsSinceEpoch); + if (newTran.atMSecsSinceEpoch == invalidMSecs() + || newTran.atMSecsSinceEpoch + newTran.offsetFromUtc * 1000 + > forLocalMSecs + sixteenHoursInMSecs) { + // Definitely not a relevant tansition: too far in the future. + break; + } tran = nextTran; - nextTran = nextTransition(tran.atMSecsSinceEpoch); + nextTran = newTran; } + + // Check we do *really* have transitions for this zone: + if (tran.atMSecsSinceEpoch != invalidMSecs()) { + + /* + So now tran is definitely before and nextTran is either after or only + slightly before. The one with the larger offset is in DST; the other in + standard time. Our hint tells us which of those to use (defaulting to + standard if no hint): try it first; if that fails, try the other; if both + fail life's tricky. + */ + Q_ASSERT(forLocalMSecs < 0 + || forLocalMSecs > tran.atMSecsSinceEpoch + tran.offsetFromUtc * 1000); + const qint64 nextStart = nextTran.atMSecsSinceEpoch; + // Work out the UTC values it might make sense to return: + nextTran.atMSecsSinceEpoch = forLocalMSecs - nextTran.offsetFromUtc * 1000; + tran.atMSecsSinceEpoch = forLocalMSecs - tran.offsetFromUtc * 1000; + + const bool nextIsDst = tran.offsetFromUtc < nextTran.offsetFromUtc; + // If that agrees with hint > 0, our first guess is to use nextTran; else tran. + const bool nextFirst = nextIsDst == (hint > 0) && nextStart != invalidMSecs(); + for (int i = 0; i < 2; i++) { + /* + On the first pass, the case we consider is what hint told us to expect + (except when hint was -1 and didn't actually tell us what to expect), + so it's likely right. We only get a second pass if the first failed, + by which time the second case, that we're trying, is likely right. If + an overwhelming majority of calls have hint == -1, the Q_LIKELY here + shall be wrong half the time; otherwise, its errors shall be rarer + than that. + */ + if (nextFirst ? i == 0 : i) { + Q_ASSERT(nextStart != invalidMSecs()); + if (Q_LIKELY(nextStart <= nextTran.atMSecsSinceEpoch)) + return nextTran; + } else { + // If next is invalid, nextFirst is false, to route us here first: + if (nextStart == invalidMSecs() || Q_LIKELY(nextStart > tran.atMSecsSinceEpoch)) + return tran; + } + } + + /* + Neither is valid (e.g. in a spring-forward's gap) and + nextTran.atMSecsSinceEpoch < nextStart <= tran.atMSecsSinceEpoch, so + 0 < tran.atMSecsSinceEpoch - nextTran.atMSecsSinceEpoch + = (nextTran.offsetFromUtc - tran.offsetFromUtc) * 1000 + */ + int dstStep = nextTran.offsetFromUtc - tran.offsetFromUtc; + Q_ASSERT(dstStep > 0); // How else could we get here ? + if (nextFirst) { // hint thought we needed nextTran, so use tran + tran.atMSecsSinceEpoch -= dstStep; + return tran; + } + nextTran.atMSecsSinceEpoch += dstStep; + return nextTran; + } + // System has transitions but not for this zone. + // Try falling back to offsetFromUtc } - if (tran.daylightTimeOffset == 0) { - // If tran is in StandardTime, then need to check if falls close to either DST transition. - // If it does, then it may need adjusting for missing hour or for second occurrence - qint64 diffPrevTran = forLocalMSecs - - (tran.atMSecsSinceEpoch + (tran.offsetFromUtc * 1000)); - qint64 diffNextTran = nextTran.atMSecsSinceEpoch + (nextTran.offsetFromUtc * 1000) - - forLocalMSecs; - if (diffPrevTran >= 0 && diffPrevTran < MSECS_TRAN_WINDOW) { - // If tran picked is for standard time check if changed from DST in last 6 hours, - // as the local msecs may be ambiguous and represent two valid utc msecs. - // If in last 6 hours then get prev tran and if diff falls within the DST offset - // then use the prev tran as we default to the FirstOccurrence - // TODO Check if faster to just always get prev tran, or if faster using 6 hour check. - Data dstTran = previousTransition(tran.atMSecsSinceEpoch); - if (dstTran.atMSecsSinceEpoch != invalidMSecs() - && dstTran.daylightTimeOffset > 0 && diffPrevTran < (dstTran.daylightTimeOffset * 1000)) - tran = dstTran; - } else if (diffNextTran >= 0 && diffNextTran <= (nextTran.daylightTimeOffset * 1000)) { - // If time falls within last hour of standard time then is actually the missing hour - // So return the next tran instead and adjust the local time to be valid - tran = nextTran; - forLocalMSecs = forLocalMSecs + (nextTran.daylightTimeOffset * 1000); + /* Bracket and refine to discover offset. */ + qint64 utcEpochMSecs; + + int early = offsetFromUtc(forLocalMSecs - sixteenHoursInMSecs); + int late = offsetFromUtc(forLocalMSecs + sixteenHoursInMSecs); + if (Q_LIKELY(early == late)) { // > 99% of the time + utcEpochMSecs = forLocalMSecs - early * 1000; + } else { + // Close to a DST transition: early > late is near a fall-back, + // early < late is near a spring-forward. + const int offsetInDst = qMax(early, late); + const int offsetInStd = qMin(early, late); + // Candidate values for utcEpochMSecs (if forLocalMSecs is valid): + const qint64 forDst = forLocalMSecs - offsetInDst * 1000; + const qint64 forStd = forLocalMSecs - offsetInStd * 1000; + // Best guess at the answer: + const qint64 hinted = hint > 0 ? forDst : forStd; + if (Q_LIKELY(offsetFromUtc(hinted) == (hint > 0 ? offsetInDst : offsetInStd))) { + utcEpochMSecs = hinted; + } else if (hint <= 0 && offsetFromUtc(forDst) == offsetInDst) { + utcEpochMSecs = forDst; + } else if (hint > 0 && offsetFromUtc(forStd) == offsetInStd) { + utcEpochMSecs = forStd; + } else { + // Invalid forLocalMSecs: in spring-forward gap. + const int dstStep = daylightTimeOffset(early < late ? + forLocalMSecs + sixteenHoursInMSecs : + forLocalMSecs - sixteenHoursInMSecs); + Q_ASSERT(dstStep); // There can't be a transition without it ! + utcEpochMSecs = (hint > 0) ? forStd - dstStep : forDst + dstStep; } } - // tran should now hold the right transition offset to use - tran.atMSecsSinceEpoch = forLocalMSecs - (tran.offsetFromUtc * 1000); - return tran; + return data(utcEpochMSecs); } bool QTimeZonePrivate::hasTransitions() const @@ -649,7 +784,7 @@ QUtcTimeZonePrivate::~QUtcTimeZonePrivate() { } -QTimeZonePrivate *QUtcTimeZonePrivate::clone() +QUtcTimeZonePrivate *QUtcTimeZonePrivate::clone() const { return new QUtcTimeZonePrivate(*this); } diff --git a/src/corelib/tools/qtimezoneprivate_android.cpp b/src/corelib/tools/qtimezoneprivate_android.cpp index 53bf90f01d..c3f8c3e0d9 100644 --- a/src/corelib/tools/qtimezoneprivate_android.cpp +++ b/src/corelib/tools/qtimezoneprivate_android.cpp @@ -88,7 +88,7 @@ void QAndroidTimeZonePrivate::init(const QByteArray &ianaId) m_id = ianaId; } -QTimeZonePrivate *QAndroidTimeZonePrivate::clone() +QAndroidTimeZonePrivate *QAndroidTimeZonePrivate::clone() const { return new QAndroidTimeZonePrivate(*this); } @@ -207,58 +207,6 @@ QTimeZonePrivate::Data QAndroidTimeZonePrivate::previousTransition(qint64 before return invalidData(); } -// Since Android does not provide an API to access transitions, -// dataForLocalTime needs to be reimplemented without direct use of transitions -QTimeZonePrivate::Data QAndroidTimeZonePrivate::dataForLocalTime(qint64 forLocalMSecs) const -{ - if (!androidTimeZone.isValid()) { - return invalidData(); - } else { - qint64 UTCepochMSecs; - - // compare the UTC time with standard offset against normal DST offset of one hour - qint64 standardUTCMSecs(forLocalMSecs - (standardTimeOffset(forLocalMSecs) * 1000)); - qint64 daylightUTCMsecs; - - // Check if daylight-saving time applies, - // checking also for DST boundaries - if (isDaylightTime(standardUTCMSecs)) { - // If DST does apply, then standardUTCMSecs will be an hour or so ahead of the real epoch time - // so check that time - daylightUTCMsecs = standardUTCMSecs - daylightTimeOffset(standardUTCMSecs)*1000; - if (isDaylightTime(daylightUTCMsecs)) { - // DST confirmed - UTCepochMSecs = daylightUTCMsecs; - } else { - // DST has just finished - UTCepochMSecs = standardUTCMSecs; - } - } else { - // Standard time indicated, but check for a false negative. - // Would a standard one-hour DST offset indicate DST? - daylightUTCMsecs = standardUTCMSecs - 3600000; // 3600000 MSECS_PER_HOUR - if (isDaylightTime(daylightUTCMsecs)) { - // DST may have just started, - // but double check against timezone's own DST offset - // (don't necessarily assume a one-hour offset) - daylightUTCMsecs = standardUTCMSecs - daylightTimeOffset(daylightUTCMsecs)*1000; - if (isDaylightTime(daylightUTCMsecs)) { - // DST confirmed - UTCepochMSecs = daylightUTCMsecs; - } else { - // false positive, apply standard time after all - UTCepochMSecs = standardUTCMSecs; - } - } else { - // confirmed standard time - UTCepochMSecs = standardUTCMSecs; - } - } - - return data(UTCepochMSecs); - } -} - QByteArray QAndroidTimeZonePrivate::systemTimeZoneId() const { QJNIObjectPrivate androidSystemTimeZone = QJNIObjectPrivate::callStaticObjectMethod("java.util.TimeZone", "getDefault", "()Ljava/util/TimeZone;"); diff --git a/src/corelib/tools/qtimezoneprivate_icu.cpp b/src/corelib/tools/qtimezoneprivate_icu.cpp index c088fe7694..a7226f2720 100644 --- a/src/corelib/tools/qtimezoneprivate_icu.cpp +++ b/src/corelib/tools/qtimezoneprivate_icu.cpp @@ -98,7 +98,7 @@ static QByteArray ucalDefaultTimeZoneId() // If successful on first or second go, resize and return if (U_SUCCESS(status)) { result.resize(size); - return result.toUtf8(); + return std::move(result).toUtf8(); } return QByteArray(); @@ -305,7 +305,7 @@ QIcuTimeZonePrivate::~QIcuTimeZonePrivate() ucal_close(m_ucal); } -QTimeZonePrivate *QIcuTimeZonePrivate::clone() +QIcuTimeZonePrivate *QIcuTimeZonePrivate::clone() const { return new QIcuTimeZonePrivate(*this); } diff --git a/src/corelib/tools/qtimezoneprivate_mac.mm b/src/corelib/tools/qtimezoneprivate_mac.mm index 5dfffeaf36..4e9a432fbf 100644 --- a/src/corelib/tools/qtimezoneprivate_mac.mm +++ b/src/corelib/tools/qtimezoneprivate_mac.mm @@ -82,7 +82,7 @@ QMacTimeZonePrivate::~QMacTimeZonePrivate() [m_nstz release]; } -QTimeZonePrivate *QMacTimeZonePrivate::clone() +QMacTimeZonePrivate *QMacTimeZonePrivate::clone() const { return new QMacTimeZonePrivate(*this); } @@ -273,4 +273,9 @@ QList<QByteArray> QMacTimeZonePrivate::availableTimeZoneIds() const return list; } +NSTimeZone *QMacTimeZonePrivate::nsTimeZone() const +{ + return m_nstz; +} + QT_END_NAMESPACE diff --git a/src/corelib/tools/qtimezoneprivate_p.h b/src/corelib/tools/qtimezoneprivate_p.h index d06784b0f9..0038908160 100644 --- a/src/corelib/tools/qtimezoneprivate_p.h +++ b/src/corelib/tools/qtimezoneprivate_p.h @@ -60,13 +60,9 @@ #include <unicode/ucal.h> #endif -#ifdef Q_OS_MAC -#ifdef __OBJC__ -@class NSTimeZone; -#else -class NSTimeZone; -#endif // __OBJC__ -#endif // Q_OS_MAC +#ifdef Q_OS_DARWIN +Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone); +#endif // Q_OS_DARWIN #ifdef Q_OS_WIN #include <qt_windows.h> @@ -78,7 +74,7 @@ class NSTimeZone; QT_BEGIN_NAMESPACE -class Q_CORE_EXPORT QTimeZonePrivate : public QSharedData +class Q_AUTOTEST_EXPORT QTimeZonePrivate : public QSharedData { public: //Version of QTimeZone::OffsetData struct using msecs for efficiency @@ -96,7 +92,7 @@ public: QTimeZonePrivate(const QTimeZonePrivate &other); virtual ~QTimeZonePrivate(); - virtual QTimeZonePrivate *clone(); + virtual QTimeZonePrivate *clone() const; bool operator==(const QTimeZonePrivate &other) const; bool operator!=(const QTimeZonePrivate &other) const; @@ -123,7 +119,7 @@ public: virtual bool isDaylightTime(qint64 atMSecsSinceEpoch) const; virtual Data data(qint64 forMSecsSinceEpoch) const; - virtual Data dataForLocalTime(qint64 forLocalMSecs) const; + Data dataForLocalTime(qint64 forLocalMSecs, int hint) const; virtual bool hasTransitions() const; virtual Data nextTransition(qint64 afterMSecsSinceEpoch) const; @@ -191,7 +187,7 @@ public: QUtcTimeZonePrivate(const QUtcTimeZonePrivate &other); virtual ~QUtcTimeZonePrivate(); - QTimeZonePrivate *clone() Q_DECL_OVERRIDE; + QUtcTimeZonePrivate *clone() const override; Data data(qint64 forMSecsSinceEpoch) const Q_DECL_OVERRIDE; @@ -238,7 +234,7 @@ public: QIcuTimeZonePrivate(const QIcuTimeZonePrivate &other); ~QIcuTimeZonePrivate(); - QTimeZonePrivate *clone() Q_DECL_OVERRIDE; + QIcuTimeZonePrivate *clone() const override; QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType, const QLocale &locale) const Q_DECL_OVERRIDE; @@ -291,15 +287,15 @@ Q_DECL_CONSTEXPR inline bool operator!=(const QTzTransitionRule &lhs, const QTzT class Q_AUTOTEST_EXPORT QTzTimeZonePrivate Q_DECL_FINAL : public QTimeZonePrivate { + QTzTimeZonePrivate(const QTzTimeZonePrivate &) = default; public: // Create default time zone QTzTimeZonePrivate(); // Create named time zone QTzTimeZonePrivate(const QByteArray &ianaId); - QTzTimeZonePrivate(const QTzTimeZonePrivate &other); ~QTzTimeZonePrivate(); - QTimeZonePrivate *clone() Q_DECL_OVERRIDE; + QTzTimeZonePrivate *clone() const override; QLocale::Country country() const Q_DECL_OVERRIDE; QString comment() const Q_DECL_OVERRIDE; @@ -355,7 +351,7 @@ public: QMacTimeZonePrivate(const QMacTimeZonePrivate &other); ~QMacTimeZonePrivate(); - QTimeZonePrivate *clone() Q_DECL_OVERRIDE; + QMacTimeZonePrivate *clone() const override; QString comment() const Q_DECL_OVERRIDE; @@ -380,6 +376,8 @@ public: QList<QByteArray> availableTimeZoneIds() const Q_DECL_OVERRIDE; + NSTimeZone *nsTimeZone() const; + private: void init(const QByteArray &zoneId); @@ -406,7 +404,7 @@ public: QWinTimeZonePrivate(const QWinTimeZonePrivate &other); ~QWinTimeZonePrivate(); - QTimeZonePrivate *clone() Q_DECL_OVERRIDE; + QWinTimeZonePrivate *clone() const override; QString comment() const Q_DECL_OVERRIDE; @@ -456,7 +454,7 @@ public: QAndroidTimeZonePrivate(const QAndroidTimeZonePrivate &other); ~QAndroidTimeZonePrivate(); - QTimeZonePrivate *clone() Q_DECL_OVERRIDE; + QAndroidTimeZonePrivate *clone() const override; QString displayName(QTimeZone::TimeType timeType, QTimeZone::NameType nameType, const QLocale &locale) const Q_DECL_OVERRIDE; @@ -475,8 +473,6 @@ public: Data nextTransition(qint64 afterMSecsSinceEpoch) const Q_DECL_OVERRIDE; Data previousTransition(qint64 beforeMSecsSinceEpoch) const Q_DECL_OVERRIDE; - Data dataForLocalTime(qint64 forLocalMSecs) const Q_DECL_OVERRIDE; - QByteArray systemTimeZoneId() const Q_DECL_OVERRIDE; QList<QByteArray> availableTimeZoneIds() const Q_DECL_OVERRIDE; diff --git a/src/corelib/tools/qtimezoneprivate_tz.cpp b/src/corelib/tools/qtimezoneprivate_tz.cpp index 10b61c3a40..1714c9802f 100644 --- a/src/corelib/tools/qtimezoneprivate_tz.cpp +++ b/src/corelib/tools/qtimezoneprivate_tz.cpp @@ -199,7 +199,7 @@ static QVector<QTzTransition> parseTzTransitions(QDataStream &ds, int tzh_timecn } } else { // Parse tzh_timecnt x 4-byte transition times - int val; + qint32 val; for (int i = 0; i < tzh_timecnt && ds.status() == QDataStream::Ok; ++i) { ds >> val; transitions[i].tz_time = val; @@ -452,13 +452,31 @@ static inline bool asciiIsLetter(char ch) return ch >= 'a' && ch <= 'z'; } +namespace { + +struct PosixZone +{ + enum { + InvalidOffset = INT_MIN, + }; + + QString name; + int offset; + + static PosixZone invalid() { return {QString(), InvalidOffset}; } + static PosixZone parse(const char *&pos, const char *end); + + bool hasValidOffset() const Q_DECL_NOTHROW { return offset != InvalidOffset; } +}; + +} // unnamed namespace + // Returns the zone name, the offset (in seconds) and advances \a begin to // where the parsing ended. Returns a zone of INT_MIN in case an offset // couldn't be read. -static QPair<QString, int> parsePosixZoneNameAndOffset(const char *&pos, const char *end) +PosixZone PosixZone::parse(const char *&pos, const char *end) { static const char offsetChars[] = "0123456789:"; - QPair<QString, int> result = qMakePair(QString(), INT_MIN); const char *nameBegin = pos; const char *nameEnd; @@ -480,7 +498,7 @@ static QPair<QString, int> parsePosixZoneNameAndOffset(const char *&pos, const c pos = nameEnd; } if (nameEnd - nameBegin < 3) - return result; // name must be at least 3 characters long + return invalid(); // name must be at least 3 characters long // zone offset, form [+-]hh:mm:ss const char *zoneBegin = pos; @@ -493,11 +511,10 @@ static QPair<QString, int> parsePosixZoneNameAndOffset(const char *&pos, const c ++zoneEnd; } - result.first = QString::fromUtf8(nameBegin, nameEnd - nameBegin); - if (zoneEnd > zoneBegin) - result.second = parsePosixOffset(zoneBegin, zoneEnd); + QString name = QString::fromUtf8(nameBegin, nameEnd - nameBegin); + const int offset = zoneEnd > zoneBegin ? parsePosixOffset(zoneBegin, zoneEnd) : InvalidOffset; pos = zoneEnd; - return result; + return {std::move(name), offset}; } static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArray &posixRule, @@ -517,19 +534,19 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra // See the section about TZ at http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html QList<QByteArray> parts = posixRule.split(','); - QPair<QString, int> stdZone, dstZone; + PosixZone stdZone, dstZone; { const QByteArray &zoneinfo = parts.at(0); const char *begin = zoneinfo.constBegin(); - stdZone = parsePosixZoneNameAndOffset(begin, zoneinfo.constEnd()); - if (stdZone.second == INT_MIN) { - stdZone.second = 0; // reset to UTC if we failed to parse + stdZone = PosixZone::parse(begin, zoneinfo.constEnd()); + if (!stdZone.hasValidOffset()) { + stdZone.offset = 0; // reset to UTC if we failed to parse } else if (begin < zoneinfo.constEnd()) { - dstZone = parsePosixZoneNameAndOffset(begin, zoneinfo.constEnd()); - if (dstZone.second == INT_MIN) { + dstZone = PosixZone::parse(begin, zoneinfo.constEnd()); + if (!dstZone.hasValidOffset()) { // if the dst offset isn't provided, it is 1 hour ahead of the standard offset - dstZone.second = stdZone.second + (60 * 60); + dstZone.offset = stdZone.offset + (60 * 60); } } } @@ -538,10 +555,10 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra if (parts.count() == 1) { QTimeZonePrivate::Data data; data.atMSecsSinceEpoch = lastTranMSecs; - data.offsetFromUtc = stdZone.second; - data.standardTimeOffset = stdZone.second; + data.offsetFromUtc = stdZone.offset; + data.standardTimeOffset = stdZone.offset; data.daylightTimeOffset = 0; - data.abbreviation = stdZone.first; + data.abbreviation = stdZone.name; result << data; return result; } @@ -568,18 +585,18 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra for (int year = startYear; year <= endYear; ++year) { QTimeZonePrivate::Data dstData; QDateTime dst(calculatePosixDate(dstDateRule, year), dstTime, Qt::UTC); - dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - (stdZone.second * 1000); - dstData.offsetFromUtc = dstZone.second; - dstData.standardTimeOffset = stdZone.second; - dstData.daylightTimeOffset = dstZone.second - stdZone.second; - dstData.abbreviation = dstZone.first; + dstData.atMSecsSinceEpoch = dst.toMSecsSinceEpoch() - (stdZone.offset * 1000); + dstData.offsetFromUtc = dstZone.offset; + dstData.standardTimeOffset = stdZone.offset; + dstData.daylightTimeOffset = dstZone.offset - stdZone.offset; + dstData.abbreviation = dstZone.name; QTimeZonePrivate::Data stdData; QDateTime std(calculatePosixDate(stdDateRule, year), stdTime, Qt::UTC); - stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - (dstZone.second * 1000); - stdData.offsetFromUtc = stdZone.second; - stdData.standardTimeOffset = stdZone.second; + stdData.atMSecsSinceEpoch = std.toMSecsSinceEpoch() - (dstZone.offset * 1000); + stdData.offsetFromUtc = stdZone.offset; + stdData.standardTimeOffset = stdZone.offset; stdData.daylightTimeOffset = 0; - stdData.abbreviation = stdZone.first; + stdData.abbreviation = stdZone.name; // Part of the high year will overflow if (year == 292278994 && (dstData.atMSecsSinceEpoch < 0 || stdData.atMSecsSinceEpoch < 0)) { if (dstData.atMSecsSinceEpoch > 0) { @@ -598,37 +615,21 @@ static QVector<QTimeZonePrivate::Data> calculatePosixTransitions(const QByteArra // Create the system default time zone QTzTimeZonePrivate::QTzTimeZonePrivate() -#if QT_CONFIG(icu) - : m_icu(0) -#endif { init(systemTimeZoneId()); } // Create a named time zone QTzTimeZonePrivate::QTzTimeZonePrivate(const QByteArray &ianaId) -#if QT_CONFIG(icu) - : m_icu(0) -#endif { init(ianaId); } -QTzTimeZonePrivate::QTzTimeZonePrivate(const QTzTimeZonePrivate &other) - : QTimeZonePrivate(other), m_tranTimes(other.m_tranTimes), - m_tranRules(other.m_tranRules), m_abbreviations(other.m_abbreviations), -#if QT_CONFIG(icu) - m_icu(other.m_icu), -#endif - m_posixRule(other.m_posixRule) -{ -} - QTzTimeZonePrivate::~QTzTimeZonePrivate() { } -QTimeZonePrivate *QTzTimeZonePrivate::clone() +QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const { return new QTzTimeZonePrivate(*this); } @@ -725,19 +726,61 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId) } } - // Now for each transition time calculate our rule and save them - m_tranTimes.reserve(tranList.count()); - for (const QTzTransition &tz_tran : qAsConst(tranList)) { + // Now for each transition time calculate and store our rule: + const int tranCount = tranList.count();; + m_tranTimes.reserve(tranCount); + // The DST offset when in effect: usually stable, usually an hour: + int lastDstOff = 3600; + for (int i = 0; i < tranCount; i++) { + const QTzTransition &tz_tran = tranList.at(i); QTzTransitionTime tran; QTzTransitionRule rule; const QTzType tz_type = typeList.at(tz_tran.tz_typeind); // Calculate the associated Rule - if (!tz_type.tz_isdst) + if (!tz_type.tz_isdst) { utcOffset = tz_type.tz_gmtoff; + } else if (Q_UNLIKELY(tz_type.tz_gmtoff != utcOffset + lastDstOff)) { + /* + This might be a genuine change in DST offset, but could also be + DST starting at the same time as the standard offset changed. See + if DST's end gives a more plausible utcOffset (i.e. one closer to + the last we saw, or a simple whole hour): + */ + // Standard offset inferred from net offset and expected DST offset: + const int inferStd = tz_type.tz_gmtoff - lastDstOff; // != utcOffset + for (int j = i + 1; j < tranCount; j++) { + const QTzType new_type = typeList.at(tranList.at(j).tz_typeind); + if (!new_type.tz_isdst) { + const int newUtc = new_type.tz_gmtoff; + if (newUtc == utcOffset) { + // DST-end can't help us, avoid lots of messy checks. + // else: See if the end matches the familiar DST offset: + } else if (newUtc == inferStd) { + utcOffset = newUtc; + // else: let either end shift us to one hour as DST offset: + } else if (tz_type.tz_gmtoff - 3600 == utcOffset) { + // Start does it + } else if (tz_type.tz_gmtoff - 3600 == newUtc) { + utcOffset = newUtc; // End does it + // else: prefer whichever end gives DST offset closer to + // last, but consider any offset > 0 "closer" than any <= 0: + } else if (newUtc < tz_type.tz_gmtoff + ? (utcOffset >= tz_type.tz_gmtoff + || qAbs(newUtc - inferStd) < qAbs(utcOffset - inferStd)) + : (utcOffset >= tz_type.tz_gmtoff + && qAbs(newUtc - inferStd) < qAbs(utcOffset - inferStd))) { + utcOffset = newUtc; + } + break; + } + } + lastDstOff = tz_type.tz_gmtoff - utcOffset; + } rule.stdOffset = utcOffset; rule.dstOffset = tz_type.tz_gmtoff - utcOffset; rule.abbreviationIndex = tz_type.tz_abbrind; + // If the rule already exist then use that, otherwise add it int ruleIndex = m_tranRules.indexOf(rule); if (ruleIndex == -1) { diff --git a/src/corelib/tools/qtimezoneprivate_win.cpp b/src/corelib/tools/qtimezoneprivate_win.cpp index 4febeda537..f963e10333 100644 --- a/src/corelib/tools/qtimezoneprivate_win.cpp +++ b/src/corelib/tools/qtimezoneprivate_win.cpp @@ -299,7 +299,7 @@ static QByteArray windowsSystemZoneId() id = readRegistryString(key, L"TimeZoneKeyName"); RegCloseKey(key); if (!id.isEmpty()) - return id.toUtf8(); + return std::move(id).toUtf8(); } // On XP we have to iterate over the zones until we find a match on @@ -415,7 +415,7 @@ QWinTimeZonePrivate::~QWinTimeZonePrivate() { } -QTimeZonePrivate *QWinTimeZonePrivate::clone() +QWinTimeZonePrivate *QWinTimeZonePrivate::clone() const { return new QWinTimeZonePrivate(*this); } diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index 8b9df7c12b..d99eebd4b9 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -165,6 +165,16 @@ public: } } + void append(T &&t) { + if (s == a) + realloc(s, s << 1); + const int idx = s++; + if (QTypeInfo<T>::isComplex) + new (ptr + idx) T(std::move(t)); + else + ptr[idx] = std::move(t); + } + void append(const T *buf, int size); inline QVarLengthArray<T, Prealloc> &operator<<(const T &t) { append(t); return *this; } @@ -218,6 +228,7 @@ public: // STL compatibility: inline bool empty() const { return isEmpty(); } inline void push_back(const T &t) { append(t); } + void push_back(T &&t) { append(std::move(t)); } inline void pop_back() { removeLast(); } inline T &front() { return first(); } inline const T &front() const { return first(); } @@ -363,7 +374,7 @@ Q_OUTOFLINE_TEMPLATE void QVarLengthArray<T, Prealloc>::realloc(int asize, int a a = Prealloc; } s = 0; - if (QTypeInfo<T>::isStatic) { + if (!QTypeInfoQuery<T>::isRelocatable) { QT_TRY { // copy all the old elements while (s < copySize) { @@ -456,7 +467,7 @@ Q_OUTOFLINE_TEMPLATE typename QVarLengthArray<T, Prealloc>::iterator QVarLengthA if (n != 0) { resize(s + n); const T copy(t); - if (QTypeInfo<T>::isStatic) { + if (!QTypeInfoQuery<T>::isRelocatable) { T *b = ptr + offset; T *j = ptr + s; T *i = j - n; diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index 5e53a969e8..127afcd069 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -303,6 +303,34 @@ */ /*! + \fn void QVarLengthArray::append(T &&t) + \overload append + \since 5.9 + + \note Unlike the lvalue overload of append(), passing a reference to + an object that is already an element of \c *this leads to undefined + behavior: + + \code + vla.append(std::move(vla[0])); // BUG: passing an object that is already in the container + \endcode +*/ + +/*! + \fn void QVarLengthArray::push_back(T &&t) + \overload push_back + \since 5.9 + + \note Unlike the lvalue overload of push_back(), passing a reference to + an object that is already an element of \c *this leads to undefined + behavior: + + \code + vla.push_back(std::move(vla[0])); // BUG: passing an object that is already in the container + \endcode +*/ + +/*! \fn inline void QVarLengthArray::removeLast() \since 4.5 diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index e345163c2f..57e80ae125 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -554,7 +554,7 @@ void QVector<T>::reallocData(const int asize, const int aalloc, QArrayData::Allo T *srcEnd = asize > d->size ? d->end() : d->begin() + asize; T *dst = x->begin(); - if (QTypeInfo<T>::isStatic || (isShared && QTypeInfo<T>::isComplex)) { + if (!QTypeInfoQuery<T>::isRelocatable || (isShared && QTypeInfo<T>::isComplex)) { // we can not move the data, we need to copy construct it while (srcBegin != srcEnd) { new (dst++) T(*srcBegin++); @@ -599,7 +599,7 @@ void QVector<T>::reallocData(const int asize, const int aalloc, QArrayData::Allo } if (d != x) { if (!d->ref.deref()) { - if (QTypeInfo<T>::isStatic || !aalloc || (isShared && QTypeInfo<T>::isComplex)) { + if (!QTypeInfoQuery<T>::isRelocatable || !aalloc || (isShared && QTypeInfo<T>::isComplex)) { // data was copy constructed, we need to call destructors // or if !alloc we did nothing to the old 'd'. freeData(d); @@ -698,7 +698,7 @@ typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, c const T copy(t); if (!isDetached() || d->size + n > int(d->alloc)) reallocData(d->size, d->size + n, QArrayData::Grow); - if (QTypeInfo<T>::isStatic) { + if (!QTypeInfoQuery<T>::isRelocatable) { T *b = d->end(); T *i = d->end() + n; while (i != b) @@ -747,7 +747,7 @@ typename QVector<T>::iterator QVector<T>::erase(iterator abegin, iterator aend) detach(); abegin = d->begin() + itemsUntouched; aend = abegin + itemsToErase; - if (QTypeInfo<T>::isStatic) { + if (!QTypeInfoQuery<T>::isRelocatable) { iterator moveBegin = abegin + itemsToErase; iterator moveEnd = d->end(); while (moveBegin != moveEnd) { diff --git a/src/corelib/tools/qvsnprintf.cpp b/src/corelib/tools/qvsnprintf.cpp index 472b1f282c..43a21771a1 100644 --- a/src/corelib/tools/qvsnprintf.cpp +++ b/src/corelib/tools/qvsnprintf.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE -#ifndef QT_VSNPRINTF +#if !defined(QT_VSNPRINTF) || defined(Q_CLANG_QDOC) /*! \relates QByteArray diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 1c9b34dacd..b93ec824ed 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -125,7 +125,6 @@ else:unix { } else:win32 { SOURCES += tools/qlocale_win.cpp - winphone: LIBS_PRIVATE += -lWindowsPhoneGlobalizationUtil winrt-*-msvc2013: LIBS += advapi32.lib } else:integrity { SOURCES += tools/qlocale_unix.cpp @@ -172,7 +171,7 @@ qtConfig(timezone) { } qtConfig(regularexpression) { - QMAKE_USE_PRIVATE += pcre + QMAKE_USE_PRIVATE += pcre2 HEADERS += tools/qregularexpression.h SOURCES += tools/qregularexpression.cpp |