diff options
Diffstat (limited to 'src/corelib/tools/qstring.cpp')
-rw-r--r-- | src/corelib/tools/qstring.cpp | 1676 |
1 files changed, 1354 insertions, 322 deletions
diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 2c96a8d33c..25f829c38c 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -72,6 +72,7 @@ #include <stdlib.h> #include <stdio.h> #include <stdarg.h> +#include <wchar.h> #include "qchar.cpp" #include "qstringmatcher.cpp" @@ -152,21 +153,65 @@ static inline int qt_string_count(const QChar *haystack, int haystackLen, QChar needle, Qt::CaseSensitivity cs); static inline int qt_find_latin1_string(const QChar *hay, int size, QLatin1String needle, int from, Qt::CaseSensitivity cs); -static inline bool qt_starts_with(const QChar *haystack, int haystackLen, - const QChar *needle, int needleLen, Qt::CaseSensitivity cs); -static inline bool qt_starts_with(const QChar *haystack, int haystackLen, - QLatin1String needle, Qt::CaseSensitivity cs); -static inline bool qt_ends_with(const QChar *haystack, int haystackLen, - const QChar *needle, int needleLen, Qt::CaseSensitivity cs); -static inline bool qt_ends_with(const QChar *haystack, int haystackLen, - QLatin1String needle, Qt::CaseSensitivity cs); +static inline bool qt_starts_with(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs); +static inline bool qt_starts_with(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs); +static inline bool qt_starts_with(QStringView haystack, QChar needle, Qt::CaseSensitivity cs); +static inline bool qt_ends_with(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs); +static inline bool qt_ends_with(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs); +static inline bool qt_ends_with(QStringView haystack, QChar needle, Qt::CaseSensitivity cs); + +qsizetype QtPrivate::qustrlen(const ushort *str) Q_DECL_NOTHROW +{ + qsizetype result = 0; + +#ifdef __SSE2__ + // find the 16-byte alignment immediately prior or equal to str + quintptr misalignment = quintptr(str) & 0xf; + Q_ASSERT((misalignment & 1) == 0); + const ushort *ptr = str - (misalignment / 2); + + // load 16 bytes and see if we have a null + // (aligned loads can never segfault) + const __m128i zeroes = _mm_setzero_si128(); + __m128i data = _mm_load_si128(reinterpret_cast<const __m128i *>(ptr)); + __m128i comparison = _mm_cmpeq_epi16(data, zeroes); + quint32 mask = _mm_movemask_epi8(comparison); + + // ignore the result prior to the beginning of str + mask >>= misalignment; + + // Have we found something in the first block? Need to handle it now + // because of the left shift above. + if (mask) + return qCountTrailingZeroBits(quint32(mask)) / 2; + + do { + ptr += 8; + data = _mm_load_si128(reinterpret_cast<const __m128i *>(ptr)); + + comparison = _mm_cmpeq_epi16(data, zeroes); + mask = _mm_movemask_epi8(comparison); + } while (mask == 0); + + // found a null + uint idx = qCountTrailingZeroBits(quint32(mask)); + return ptr - str + idx / 2; +#endif + + if (sizeof(wchar_t) == sizeof(ushort)) + return wcslen(reinterpret_cast<const wchar_t *>(str)); + + while (*str++) + ++result; + return result; +} #if defined(Q_COMPILER_LAMBDA) && !defined(__OPTIMIZE_SIZE__) namespace { template <uint MaxCount> struct UnrollTailLoop { - template <typename RetType, typename Functor1, typename Functor2> - static inline RetType exec(int count, RetType returnIfExited, Functor1 loopCheck, Functor2 returnIfFailed, int i = 0) + template <typename RetType, typename Functor1, typename Functor2, typename Number> + static inline RetType exec(Number count, RetType returnIfExited, Functor1 loopCheck, Functor2 returnIfFailed, Number i = 0) { /* equivalent to: * while (count--) { @@ -188,24 +233,169 @@ template <uint MaxCount> struct UnrollTailLoop return UnrollTailLoop<MaxCount - 1>::exec(count - 1, returnIfExited, loopCheck, returnIfFailed, i + 1); } - template <typename Functor> - static inline void exec(int count, Functor code) + template <typename Functor, typename Number> + static inline void exec(Number count, Functor code) { /* equivalent to: - * for (int i = 0; i < count; ++i) + * for (Number i = 0; i < count; ++i) * code(i); */ - exec(count, 0, [=](int i) -> bool { code(i); return false; }, [](int) { return 0; }); + exec(count, 0, [=](Number i) -> bool { code(i); return false; }, [](Number) { return 0; }); } }; -template <> template <typename RetType, typename Functor1, typename Functor2> -inline RetType UnrollTailLoop<0>::exec(int, RetType returnIfExited, Functor1, Functor2, int) +template <> template <typename RetType, typename Functor1, typename Functor2, typename Number> +inline RetType UnrollTailLoop<0>::exec(Number, RetType returnIfExited, Functor1, Functor2, Number) { return returnIfExited; } } #endif +#ifdef __SSE2__ +static bool simdTestMask(const char *&ptr, const char *end, quint32 maskval) +{ +# if defined(__AVX2__) + // AVX2 implementation: test 32 bytes at a time + const __m256i mask256 = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(maskval)); + while (ptr + 32 < end) { + __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr)); + if (!_mm256_testz_si256(mask256, data)) + return false; + ptr += 32; + } + + const __m128i mask = _mm256_castsi256_si128(mask256); +# elif defined(__SSE4_1__) + // SSE 4.1 implementation: test 32 bytes at a time (two 16-byte + // comparisons, unrolled) + const __m128i mask = _mm_set1_epi32(maskval); + while (ptr + 32 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); + __m128i data2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 16)); + if (!_mm_testz_si128(mask, data1)) + return false; + if (!_mm_testz_si128(mask, data2)) + return false; + ptr += 32; + } +# endif +# if defined(__SSE4_1__) + // AVX2 and SSE4.1: final 16-byte comparison + if (ptr + 16 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); + if (!_mm_testz_si128(mask, data1)) + return false; + ptr += 16; + } +# else + // SSE2 implementation: test 16 bytes at a time. + const __m128i mask = _mm_set1_epi32(maskval); + while (ptr + 16 < end) { + __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); + __m128i masked = _mm_andnot_si128(mask, data); + __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128()); + if (quint16(_mm_movemask_epi8(comparison)) != 0xffff) + return false; + ptr += 16; + } +# endif + + return true; +} +#endif + +bool QtPrivate::isAscii(QLatin1String s) Q_DECL_NOTHROW +{ + const char *ptr = s.begin(); + const char *end = s.end(); + +#if defined(__AVX2__) + if (!simdTestMask(ptr, end, 0x80808080)) + return false; +#elif defined(__SSE2__) + // Testing for the high bit can be done efficiently with just PMOVMSKB + while (ptr + 16 < end) { + __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); + quint32 mask = _mm_movemask_epi8(data); + if (mask) + return false; + ptr += 16; + } +#endif + + while (ptr + 4 < end) { + quint32 data = qFromUnaligned<quint32>(ptr); + if (data & 0x80808080U) + return false; + ptr += 4; + } + + while (ptr != end) { + if (quint8(*ptr++) & 0x80) + return false; + } + return true; +} + +bool QtPrivate::isAscii(QStringView s) Q_DECL_NOTHROW +{ + const QChar *ptr = s.begin(); + const QChar *end = s.end(); + +#ifdef __SSE2__ + const char *ptr8 = reinterpret_cast<const char *>(ptr); + const char *end8 = reinterpret_cast<const char *>(end); + if (!simdTestMask(ptr8, end8, 0xff80ff80)) + return false; + ptr = reinterpret_cast<const QChar *>(ptr8); +#endif + + while (ptr != end) { + if ((*ptr++).unicode() & 0xff80) + return false; + } + return true; +} + +bool QtPrivate::isLatin1(QStringView s) Q_DECL_NOTHROW +{ + const QChar *ptr = s.begin(); + const QChar *end = s.end(); + +#if defined(__SSE4_1__) + const char *ptr8 = reinterpret_cast<const char *>(ptr); + const char *end8 = reinterpret_cast<const char *>(end); + if (!simdTestMask(ptr8, end8, 0xff00ff00)) + return false; + ptr = reinterpret_cast<const QChar *>(ptr8); +#elif defined(__SSE2__) + // Testing if every other byte is non-zero can be done efficiently by + // using PUNPCKHBW (unpack high order bytes) and comparing that to zero. + while (ptr + 32 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); + __m128i data2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 16)); + __m128i high = _mm_unpackhi_epi8(data1, data2); + __m128i comparison = _mm_cmpeq_epi16(high, _mm_setzero_si128()); + if (_mm_movemask_epi8(comparison)) + return false; + ptr += 16; + } + if (ptr + 16 < end) { + __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr)); + __m128i high = _mm_unpackhi_epi8(data1, data1); + __m128i comparison = _mm_cmpeq_epi16(high, _mm_setzero_si128()); + if (_mm_movemask_epi8(comparison)) + return false; + } +#endif + + while (ptr != end) { + if ((*ptr++).unicode() > 0xff) + return false; + } + return true; +} + // conversion between Latin 1 and UTF-16 void qt_from_latin1(ushort *dst, const char *str, size_t size) Q_DECL_NOTHROW { @@ -374,16 +564,16 @@ static void qt_to_latin1(uchar *dst, const ushort *src, int length) } // Unicode case-insensitive comparison -static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const ushort *be) +static int ucstricmp(const QChar *a, const QChar *ae, const QChar *b, const QChar *be) { if (a == b) return (ae - be); if (a == 0) - return 1; + return be - b; if (b == 0) - return -1; + return a - ae; - const ushort *e = ae; + const QChar *e = ae; if (be - b < ae - a) e = a + (be - b); @@ -393,7 +583,7 @@ static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const u // qDebug() << hex << alast << blast; // qDebug() << hex << "*a=" << *a << "alast=" << alast << "folded=" << foldCase (*a, alast); // qDebug() << hex << "*b=" << *b << "blast=" << blast << "folded=" << foldCase (*b, blast); - int diff = foldCase(*a, alast) - foldCase(*b, blast); + int diff = foldCase(a->unicode(), alast) - foldCase(b->unicode(), blast); if ((diff)) return diff; ++a; @@ -408,22 +598,19 @@ static int ucstricmp(const ushort *a, const ushort *ae, const ushort *b, const u } // Case-insensitive comparison between a Unicode string and a QLatin1String -static int ucstricmp(const ushort *a, const ushort *ae, const uchar *b, const uchar *be) +static int ucstricmp(const QChar *a, const QChar *ae, const char *b, const char *be) { - if (a == 0) { - if (b == 0) - return 0; - return 1; - } - if (b == 0) - return -1; + if (!a) + return be - b; + if (!b) + return a - ae; - const ushort *e = ae; + auto e = ae; if (be - b < ae - a) e = a + (be - b); while (a < e) { - int diff = foldCase(*a) - foldCase(*b); + int diff = foldCase(a->unicode()) - foldCase(uchar(*b)); if ((diff)) return diff; ++a; @@ -445,7 +632,7 @@ extern "C" int qt_ucstrncmp_mips_dsp_asm(const ushort *a, #endif // Unicode case-sensitive compare two same-sized strings -static int ucstrncmp(const QChar *a, const QChar *b, int l) +static int ucstrncmp(const QChar *a, const QChar *b, size_t l) { #ifdef __OPTIMIZE_SIZE__ const QChar *end = a + l; @@ -458,6 +645,7 @@ static int ucstrncmp(const QChar *a, const QChar *b, int l) return 0; #else #if defined(__mips_dsp) + Q_STATIC_ASSERT(sizeof(uint) == sizeof(size_t)); if (l >= 8) { return qt_ucstrncmp_mips_dsp_asm(reinterpret_cast<const ushort*>(a), reinterpret_cast<const ushort*>(b), @@ -484,13 +672,11 @@ static int ucstrncmp(const QChar *a, const QChar *b, int l) - reinterpret_cast<const QChar *>(ptr + distance + idx)->unicode(); } } -# if defined(Q_COMPILER_LAMBDA) - const auto &lambda = [=](int i) -> int { + const auto lambda = [=](size_t i) -> int { return reinterpret_cast<const QChar *>(ptr)[i].unicode() - reinterpret_cast<const QChar *>(ptr + distance)[i].unicode(); }; 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) { @@ -511,7 +697,7 @@ static int ucstrncmp(const QChar *a, const QChar *b, int l) } l &= 7; } - const auto &lambda = [=](int i) -> int { + const auto lambda = [=](size_t i) -> int { return a[i].unicode() - b[i].unicode(); }; return UnrollTailLoop<7>::exec(l, 0, lambda, lambda); @@ -565,7 +751,7 @@ static int ucstrncmp(const QChar *a, const QChar *b, int l) #endif } -static int ucstrncmp(const QChar *a, const uchar *c, int l) +static int ucstrncmp(const QChar *a, const uchar *c, size_t l) { const ushort *uc = reinterpret_cast<const ushort *>(a); const ushort *e = uc + l; @@ -640,8 +826,8 @@ static int ucstrncmp(const QChar *a, const uchar *c, int l) uc += offset; c += offset; -# if defined(Q_COMPILER_LAMBDA) && !defined(__OPTIMIZE_SIZE__) - const auto &lambda = [=](int i) { return uc[i] - ushort(c[i]); }; +# if !defined(__OPTIMIZE_SIZE__) + const auto lambda = [=](size_t i) { return uc[i] - ushort(c[i]); }; return UnrollTailLoop<MaxTailLength>::exec(e - uc, 0, lambda, lambda); # endif #endif @@ -656,35 +842,142 @@ static int ucstrncmp(const QChar *a, const uchar *c, int l) return 0; } +template <typename Number> +Q_DECL_CONSTEXPR int lencmp(Number lhs, Number rhs) Q_DECL_NOTHROW +{ + return lhs == rhs ? 0 : + lhs > rhs ? 1 : + /* else */ -1 ; +} + // Unicode case-sensitive comparison -static int ucstrcmp(const QChar *a, int alen, const QChar *b, int blen) +static int ucstrcmp(const QChar *a, size_t alen, const QChar *b, size_t blen) { if (a == b && alen == blen) return 0; - int l = qMin(alen, blen); + const size_t l = qMin(alen, blen); int cmp = ucstrncmp(a, b, l); - return cmp ? cmp : (alen-blen); + return cmp ? cmp : lencmp(alen, blen); } -// Unicode case-insensitive compare two same-sized strings -static int ucstrnicmp(const ushort *a, const ushort *b, int l) +static int ucstrcmp(const QChar *a, size_t alen, const char *b, size_t blen) { - return ucstricmp(a, a + l, b, b + l); + const size_t l = qMin(alen, blen); + const int cmp = ucstrncmp(a, reinterpret_cast<const uchar*>(b), l); + return cmp ? cmp : lencmp(alen, blen); } -static bool qMemEquals(const quint16 *a, const quint16 *b, int length) +static int qt_compare_strings(QStringView lhs, QStringView rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { - if (a == b || !length) - return true; + if (cs == Qt::CaseSensitive) + return ucstrcmp(lhs.begin(), lhs.size(), rhs.begin(), rhs.size()); + else + return ucstricmp(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} - return ucstrncmp(reinterpret_cast<const QChar *>(a), reinterpret_cast<const QChar *>(b), length) == 0; +static int qt_compare_strings(QStringView lhs, QLatin1String rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + if (cs == Qt::CaseSensitive) + return ucstrcmp(lhs.begin(), lhs.size(), rhs.begin(), rhs.size()); + else + return ucstricmp(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } -static int ucstrcmp(const QChar *a, int alen, const uchar *b, int blen) +static int qt_compare_strings(QLatin1String lhs, QStringView rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { - int l = qMin(alen, blen); - int cmp = ucstrncmp(a, b, l); - return cmp ? cmp : (alen-blen); + return -qt_compare_strings(rhs, lhs, cs); +} + +static int qt_compare_strings(QLatin1String lhs, QLatin1String rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + if (lhs.isEmpty()) + return lencmp(0, rhs.size()); + const auto l = std::min(lhs.size(), rhs.size()); + int r; + if (cs == Qt::CaseSensitive) + r = qstrncmp(lhs.data(), rhs.data(), l); + else + r = qstrnicmp(lhs.data(), rhs.data(), l); + return r ? r : lencmp(lhs.size(), rhs.size()); +} + +/*! + \relates QStringView + \internal + \since 5.10 + + Returns an integer that compares to 0 as \a lhs compares to \a rhs. + + If \a cs is Qt::CaseSensitive (the default), the comparison is case-sensitive; + otherwise the comparison is case-insensitive. + + Case-sensitive comparison is based exclusively on the numeric Unicode values + of the characters and is very fast, but is not what a human would expect. + Consider sorting user-visible strings with QString::localeAwareCompare(). +*/ +int QtPrivate::compareStrings(QStringView lhs, QStringView rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_compare_strings(lhs, rhs, cs); +} + +/*! + \relates QStringView + \internal + \since 5.10 + \overload + + Returns an integer that compares to 0 as \a lhs compares to \a rhs. + + If \a cs is Qt::CaseSensitive (the default), the comparison is case-sensitive; + otherwise the comparison is case-insensitive. + + Case-sensitive comparison is based exclusively on the numeric Unicode values + of the characters and is very fast, but is not what a human would expect. + Consider sorting user-visible strings with QString::localeAwareCompare(). +*/ +int QtPrivate::compareStrings(QStringView lhs, QLatin1String rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_compare_strings(lhs, rhs, cs); +} + +/*! + \relates QStringView + \internal + \since 5.10 + \overload + + Returns an integer that compares to 0 as \a lhs compares to \a rhs. + + If \a cs is Qt::CaseSensitive (the default), the comparison is case-sensitive; + otherwise the comparison is case-insensitive. + + Case-sensitive comparison is based exclusively on the numeric Unicode values + of the characters and is very fast, but is not what a human would expect. + Consider sorting user-visible strings with QString::localeAwareCompare(). +*/ +int QtPrivate::compareStrings(QLatin1String lhs, QStringView rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_compare_strings(lhs, rhs, cs); +} + +/*! + \relates QStringView + \internal + \since 5.10 + \overload + + Returns an integer that compares to 0 as \a lhs compares to \a rhs. + + If \a cs is Qt::CaseSensitive (the default), the comparison is case-sensitive; + otherwise the comparison is case-insensitive. + + Case-sensitive comparison is based exclusively on the numeric Latin-1 values + of the characters and is very fast, but is not what a human would expect. + Consider sorting user-visible strings with QString::localeAwareCompare(). +*/ +int QtPrivate::compareStrings(QLatin1String lhs, QLatin1String rhs, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_compare_strings(lhs, rhs, cs); } /*! @@ -1502,7 +1795,7 @@ int QString::toUcs4_helper(const ushort *uc, int length, uint *out) { int count = 0; - QStringIterator i(reinterpret_cast<const QChar *>(uc), reinterpret_cast<const QChar *>(uc + length)); + QStringIterator i(QStringView(uc, length)); while (i.hasNext()) out[count++] = i.next(); @@ -2293,6 +2586,21 @@ QString &QString::remove(int pos, int len) return *this; } +template<typename T> +static void removeStringImpl(QString &s, const T &needle, Qt::CaseSensitivity cs) +{ + const int needleSize = needle.size(); + if (needleSize) { + if (needleSize == 1) { + s.remove(needle.front(), cs); + } else { + int i = 0; + while ((i = s.indexOf(needle, i, cs)) != -1) + s.remove(i, needleSize); + } + } +} + /*! Removes every occurrence of the given \a str string in this string, and returns a reference to this string. @@ -2306,11 +2614,27 @@ QString &QString::remove(int pos, int len) */ QString &QString::remove(const QString &str, Qt::CaseSensitivity cs) { - if (str.d->size) { - int i = 0; - while ((i = indexOf(str, i, cs)) != -1) - remove(i, str.d->size); - } + removeStringImpl(*this, str, cs); + return *this; +} + +/*! + \since 5.11 + \overload + + Removes every occurrence of the given \a str string in this + string, and returns a reference to this string. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + This is the same as \c replace(str, "", cs). + + \sa replace() +*/ +QString &QString::remove(QLatin1String str, Qt::CaseSensitivity cs) +{ + removeStringImpl(*this, str, cs); return *this; } @@ -2789,7 +3113,7 @@ bool operator==(const QString &s1, const QString &s2) Q_DECL_NOTHROW if (s1.d->size != s2.d->size) return false; - return qMemEquals(s1.d->data(), s2.d->data(), s1.d->size); + return qt_compare_strings(s1, s2, Qt::CaseSensitive) == 0; } /*! @@ -2802,10 +3126,7 @@ bool QString::operator==(QLatin1String other) const Q_DECL_NOTHROW if (d->size != other.size()) return false; - if (!other.size()) - return isEmpty(); - - return compare_helper(data(), size(), other, Qt::CaseSensitive) == 0; + return qt_compare_strings(*this, other, Qt::CaseSensitive) == 0; } /*! \fn bool QString::operator==(const QByteArray &other) const @@ -2850,8 +3171,9 @@ bool QString::operator==(QLatin1String other) const Q_DECL_NOTHROW */ bool operator<(const QString &s1, const QString &s2) Q_DECL_NOTHROW { - return ucstrcmp(s1.constData(), s1.length(), s2.constData(), s2.length()) < 0; + return qt_compare_strings(s1, s2, Qt::CaseSensitive) < 0; } + /*! \overload operator<() @@ -2860,11 +3182,7 @@ bool operator<(const QString &s1, const QString &s2) Q_DECL_NOTHROW */ bool QString::operator<(QLatin1String other) const Q_DECL_NOTHROW { - const uchar *c = (const uchar *) other.latin1(); - if (!c || *c == 0) - return false; - - return compare_helper(data(), size(), other, Qt::CaseSensitive) < 0; + return qt_compare_strings(*this, other, Qt::CaseSensitive) < 0; } /*! \fn bool QString::operator<(const QByteArray &other) const @@ -2965,11 +3283,7 @@ bool QString::operator<(QLatin1String other) const Q_DECL_NOTHROW */ bool QString::operator>(QLatin1String other) const Q_DECL_NOTHROW { - const uchar *c = (const uchar *) other.latin1(); - if (!c || *c == '\0') - return !isEmpty(); - - return compare_helper(data(), size(), other, Qt::CaseSensitive) > 0; + return qt_compare_strings(*this, other, Qt::CaseSensitive) > 0; } /*! \fn bool QString::operator>(const QByteArray &other) const @@ -3166,11 +3480,12 @@ int qFindString( return qFindStringBoyerMoore(haystack0, haystackLen, from, needle0, needleLen, cs); + auto sv = [sl](const ushort *v) { return QStringView(v, sl); }; /* We use some hashing for efficiency's sake. Instead of comparing strings, we compare the hash value of str with that of a part of this QString. Only if that matches, we call - ucstrncmp() or ucstrnicmp(). + qt_string_compare(). */ const ushort *needle = (const ushort *)needle0; const ushort *haystack = (const ushort *)haystack0 + from; @@ -3189,7 +3504,7 @@ int qFindString( while (haystack <= end) { hashHaystack += haystack[sl_minus_1]; if (hashHaystack == hashNeedle - && ucstrncmp((const QChar *)needle, (const QChar *)haystack, sl) == 0) + && qt_compare_strings(sv(needle), sv(haystack), Qt::CaseSensitive) == 0) return haystack - (const ushort *)haystack0; REHASH(*haystack); @@ -3205,7 +3520,8 @@ int qFindString( while (haystack <= end) { hashHaystack += foldCase(haystack + sl_minus_1, haystack_start); - if (hashHaystack == hashNeedle && ucstrnicmp(needle, haystack, sl) == 0) + if (hashHaystack == hashNeedle + && qt_compare_strings(sv(needle), sv(haystack), Qt::CaseInsensitive) == 0) return haystack - (const ushort *)haystack0; REHASH(foldCase(haystack, haystack_start)); @@ -3250,6 +3566,8 @@ static int lastIndexOfHelper(const ushort *haystack, int from, const ushort *nee See indexOf() for explanations. */ + auto sv = [sl](const ushort *v) { return QStringView(v, sl); }; + const ushort *end = haystack; haystack += from; const uint sl_minus_1 = sl - 1; @@ -3268,7 +3586,7 @@ static int lastIndexOfHelper(const ushort *haystack, int from, const ushort *nee while (haystack >= end) { hashHaystack += *haystack; if (hashHaystack == hashNeedle - && ucstrncmp((const QChar *)needle, (const QChar *)haystack, sl) == 0) + && qt_compare_strings(sv(needle), sv(haystack), Qt::CaseSensitive) == 0) return haystack - end; --haystack; REHASH(haystack[sl]); @@ -3282,7 +3600,8 @@ static int lastIndexOfHelper(const ushort *haystack, int from, const ushort *nee while (haystack >= end) { hashHaystack += foldCase(haystack, end); - if (hashHaystack == hashNeedle && ucstrnicmp(needle, haystack, sl) == 0) + if (hashHaystack == hashNeedle + && qt_compare_strings(sv(haystack), sv(needle), Qt::CaseInsensitive) == 0) return haystack - end; --haystack; REHASH(foldCase(haystack + sl, end)); @@ -3913,7 +4232,7 @@ int QString::count(const QRegExp& rx) const */ int QString::indexOf(const QRegularExpression& re, int from) const { - return indexOf(re, from, Q_NULLPTR); + return indexOf(re, from, nullptr); } /*! @@ -3964,7 +4283,7 @@ int QString::indexOf(const QRegularExpression &re, int from, QRegularExpressionM */ int QString::lastIndexOf(const QRegularExpression &re, int from) const { - return lastIndexOf(re, from, Q_NULLPTR); + return lastIndexOf(re, from, nullptr); } /*! @@ -4016,7 +4335,7 @@ int QString::lastIndexOf(const QRegularExpression &re, int from, QRegularExpress */ bool QString::contains(const QRegularExpression &re) const { - return contains(re, Q_NULLPTR); + return contains(re, nullptr); } /*! @@ -4366,7 +4685,7 @@ QString QString::section(const QRegularExpression &re, int start, int end, Secti \snippet qstring/main.cpp 31 - \sa right(), mid(), startsWith() + \sa right(), mid(), startsWith(), chopped(), chop(), truncate() */ QString QString::left(int n) const { @@ -4384,7 +4703,7 @@ QString QString::left(int n) const \snippet qstring/main.cpp 48 - \sa left(), mid(), endsWith() + \sa left(), mid(), endsWith(), chopped(), chop(), truncate() */ QString QString::right(int n) const { @@ -4407,7 +4726,7 @@ QString QString::right(int n) const \snippet qstring/main.cpp 34 - \sa left(), right() + \sa left(), right(), chopped(), chop(), truncate() */ QString QString::mid(int position, int n) const @@ -4431,6 +4750,19 @@ QString QString::mid(int position, int n) const } /*! + \fn QString QString::chopped(int len) const + \since 5.10 + + Returns a substring that contains the size() - \a len leftmost characters + of this string. + + \note The behavior is undefined if \a len is negative or greater than size(). + + \sa endsWith(), left(), right(), mid(), chop(), truncate() +*/ + +#if QT_STRINGVIEW_LEVEL < 2 +/*! Returns \c true if the string starts with \a s; otherwise returns \c false. @@ -4443,16 +4775,16 @@ QString QString::mid(int position, int n) const */ bool QString::startsWith(const QString& s, Qt::CaseSensitivity cs) const { - return qt_starts_with(isNull() ? 0 : unicode(), size(), - s.isNull() ? 0 : s.unicode(), s.size(), cs); + return qt_starts_with(*this, s, cs); } +#endif /*! \overload startsWith() */ bool QString::startsWith(QLatin1String s, Qt::CaseSensitivity cs) const { - return qt_starts_with(isNull() ? 0 : unicode(), size(), s, cs); + return qt_starts_with(*this, s, cs); } /*! @@ -4463,12 +4795,10 @@ bool QString::startsWith(QLatin1String s, Qt::CaseSensitivity cs) const */ bool QString::startsWith(QChar c, Qt::CaseSensitivity cs) const { - return d->size - && (cs == Qt::CaseSensitive - ? d->data()[0] == c - : foldCase(d->data()[0]) == foldCase(c.unicode())); + return qt_starts_with(*this, c, cs); } +#if QT_STRINGVIEW_LEVEL < 2 /*! \since 4.8 \overload @@ -4482,11 +4812,26 @@ bool QString::startsWith(QChar c, Qt::CaseSensitivity cs) const */ bool QString::startsWith(const QStringRef &s, Qt::CaseSensitivity cs) const { - return qt_starts_with(isNull() ? 0 : unicode(), size(), - s.isNull() ? 0 : s.unicode(), s.size(), cs); + return qt_starts_with(*this, s, cs); } +#endif /*! + \fn bool QString::startsWith(QStringView str, Qt::CaseSensitivity cs) const + \since 5.10 + \overload + + Returns \c true if the string starts with the string-view \a str; + otherwise returns \c false. + + If \a cs is Qt::CaseSensitive (default), the search is case-sensitive; + otherwise the search is case insensitive. + + \sa endsWith() +*/ + +#if QT_STRINGVIEW_LEVEL < 2 +/*! Returns \c true if the string ends with \a s; otherwise returns \c false. @@ -4499,8 +4844,7 @@ bool QString::startsWith(const QStringRef &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); + return qt_ends_with(*this, s, cs); } /*! @@ -4516,17 +4860,29 @@ bool QString::endsWith(const QString &s, Qt::CaseSensitivity cs) const */ bool QString::endsWith(const QStringRef &s, Qt::CaseSensitivity cs) const { - return qt_ends_with(isNull() ? 0 : unicode(), size(), - s.isNull() ? 0 : s.unicode(), s.size(), cs); + return qt_ends_with(*this, s, cs); } +#endif // QT_STRINGVIEW_LEVEL < 2 +/*! + \fn bool QString::endsWith(QStringView str, Qt::CaseSensitivity cs) const + \since 5.10 + \overload endsWith() + Returns \c true if the string ends with the string view \a str; + otherwise returns \c false. + + If \a cs is Qt::CaseSensitive (default), the search is case + sensitive; otherwise the search is case insensitive. + + \sa startsWith() +*/ /*! \overload endsWith() */ bool QString::endsWith(QLatin1String s, Qt::CaseSensitivity cs) const { - return qt_ends_with(isNull() ? 0 : unicode(), size(), s, cs); + return qt_ends_with(*this, s, cs); } /*! @@ -4537,35 +4893,56 @@ bool QString::endsWith(QLatin1String s, Qt::CaseSensitivity cs) const */ bool QString::endsWith(QChar c, Qt::CaseSensitivity cs) const { - return d->size - && (cs == Qt::CaseSensitive - ? d->data()[d->size - 1] == c - : foldCase(d->data()[d->size - 1]) == foldCase(c.unicode())); + return qt_ends_with(*this, c, cs); } +static QByteArray qt_convert_to_latin1(QStringView string); + QByteArray QString::toLatin1_helper(const QString &string) { - if (Q_UNLIKELY(string.isNull())) - return QByteArray(); - - return toLatin1_helper(string.constData(), string.length()); + return qt_convert_to_latin1(string); } QByteArray QString::toLatin1_helper(const QChar *data, int length) { - QByteArray ba(length, Qt::Uninitialized); + return qt_convert_to_latin1(QStringView(data, length)); +} + +/*! + \since 5.10 + \internal + \relates QStringView + + Returns a Latin-1 representation of \a string as a QByteArray. + + The behavior is undefined if \a string contains non-Latin1 characters. + + \sa QString::toLatin1(), QStringView::toLatin1(), QtPrivate::convertToUtf8(), + QtPrivate::convertToLocal8Bit(), QtPrivate::convertToUcs4() +*/ +QByteArray QtPrivate::convertToLatin1(QStringView string) +{ + return qt_convert_to_latin1(string); +} + +static QByteArray qt_convert_to_latin1(QStringView string) +{ + if (Q_UNLIKELY(string.isNull())) + return QByteArray(); + + QByteArray ba(string.length(), Qt::Uninitialized); // since we own the only copy, we're going to const_cast the constData; // that avoids an unnecessary call to detach() and expansion code that will never get used qt_to_latin1(reinterpret_cast<uchar *>(const_cast<char *>(ba.constData())), - reinterpret_cast<const ushort *>(data), length); + reinterpret_cast<const ushort *>(string.data()), string.length()); return ba; } QByteArray QString::toLatin1_helper_inplace(QString &s) { if (!s.isDetached()) - return s.toLatin1(); + return qt_convert_to_latin1(s); // We can return our own buffer to the caller. // Conversion to Latin-1 always shrinks the buffer by half. @@ -4617,6 +4994,8 @@ QByteArray QString::toLatin1_helper_inplace(QString &s) \sa fromAscii(), toLatin1(), toUtf8(), toLocal8Bit(), QTextCodec */ +static QByteArray qt_convert_to_local_8bit(QStringView string); + /*! \fn QByteArray QString::toLocal8Bit() const @@ -4637,16 +5016,42 @@ QByteArray QString::toLatin1_helper_inplace(QString &s) QByteArray QString::toLocal8Bit_helper(const QChar *data, int size) { - if (!data) + return qt_convert_to_local_8bit(QStringView(data, size)); +} + +static QByteArray qt_convert_to_local_8bit(QStringView string) +{ + if (string.isNull()) return QByteArray(); #ifndef QT_NO_TEXTCODEC QTextCodec *localeCodec = QTextCodec::codecForLocale(); if (localeCodec) - return localeCodec->fromUnicode(data, size); + return localeCodec->fromUnicode(string); #endif // QT_NO_TEXTCODEC - return toLatin1_helper(data, size); + return qt_convert_to_latin1(string); +} + +/*! + \since 5.10 + \internal + \relates QStringView + + Returns a local 8-bit representation of \a string as a QByteArray. + + QTextCodec::codecForLocale() is used to perform the conversion from + Unicode. + + The behavior is undefined if \a string contains characters not + supported by the locale's 8-bit encoding. + + \sa QString::toLocal8Bit(), QStringView::toLocal8Bit() +*/ +QByteArray QtPrivate::convertToLocal8Bit(QStringView string) +{ + return qt_convert_to_local_8bit(string); } +static QByteArray qt_convert_to_utf8(QStringView str); /*! \fn QByteArray QString::toUtf8() const @@ -4661,12 +5066,36 @@ QByteArray QString::toLocal8Bit_helper(const QChar *data, int size) QByteArray QString::toUtf8_helper(const QString &str) { + return qt_convert_to_utf8(str); +} + +static QByteArray qt_convert_to_utf8(QStringView str) +{ if (str.isNull()) return QByteArray(); - return QUtf8::convertFromUnicode(str.constData(), str.length()); + return QUtf8::convertFromUnicode(str.data(), str.length()); +} + +/*! + \since 5.10 + \internal + \relates QStringView + + Returns a UTF-8 representation of \a string as a QByteArray. + + UTF-8 is a Unicode codec and can represent all characters in a Unicode + string like QStringView. + + \sa QString::toUtf8(), QStringView::toUtf8() +*/ +QByteArray QtPrivate::convertToUtf8(QStringView string) +{ + return qt_convert_to_utf8(string); } +static QVector<uint> qt_convert_to_ucs4(QStringView string); + /*! \since 4.2 @@ -4683,13 +5112,42 @@ QByteArray QString::toUtf8_helper(const QString &str) */ QVector<uint> QString::toUcs4() const { - QVector<uint> v(length()); - uint *a = v.data(); - int len = toUcs4_helper(d->data(), length(), a); - v.resize(len); + return qt_convert_to_ucs4(*this); +} + +static QVector<uint> qt_convert_to_ucs4(QStringView string) +{ + QVector<uint> v(string.length()); + uint *a = const_cast<uint*>(v.constData()); + QStringIterator it(string); + while (it.hasNext()) + *a++ = it.next(); + v.resize(a - v.constData()); return v; } +/*! + \since 5.10 + \internal + \relates QStringView + + Returns a UCS-4/UTF-32 representation of \a string as a QVector<uint>. + + UCS-4 is a Unicode codec and therefore it is lossless. All characters from + this string will be encoded in UCS-4. Any invalid sequence of code units in + this string is replaced by the Unicode's replacement character + (QChar::ReplacementCharacter, which corresponds to \c{U+FFFD}). + + The returned vector is not NUL terminated. + + \sa QString::toUcs4(), QStringView::toUcs4(), QtPrivate::convertToLatin1(), + QtPrivate::convertToLocal8Bit(), QtPrivate::convertToUtf8() +*/ +QVector<uint> QtPrivate::convertToUcs4(QStringView string) +{ + return qt_convert_to_ucs4(string); +} + QString::Data *QString::fromLatin1_helper(const char *str, int size) { Data *d; @@ -4980,6 +5438,42 @@ QString QString::simplified_helper(QString &str) return QStringAlgorithms<QString>::simplified_helper(str); } +namespace { + template <typename StringView> + StringView qt_trimmed(StringView s) Q_DECL_NOTHROW + { + auto begin = s.begin(); + auto end = s.end(); + QStringAlgorithms<const StringView>::trimmed_helper_positions(begin, end); + return StringView{begin, end}; + } +} + +/*! + \fn QStringView QtPrivate::trimmed(QStringView s) + \fn QLatin1String QtPrivate::trimmed(QLatin1String s) + \internal + \relates QStringView + \since 5.10 + + Returns \a s with whitespace removed from the start and the end. + + Whitespace means any character for which QChar::isSpace() returns + \c true. This includes the ASCII characters '\\t', '\\n', '\\v', + '\\f', '\\r', and ' '. + + \sa QString::trimmed(), QStringView::trimmed(), QLatin1String::trimmed() +*/ +QStringView QtPrivate::trimmed(QStringView s) Q_DECL_NOTHROW +{ + return qt_trimmed(s); +} + +QLatin1String QtPrivate::trimmed(QLatin1String s) Q_DECL_NOTHROW +{ + return qt_trimmed(s); +} + /*! \fn QString QString::trimmed() const @@ -5057,6 +5551,66 @@ modifiable reference. */ /*! + \fn QChar QString::front() const + \since 5.10 + + Returns the first character in the string. + Same as \c{at(0)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa back(), at(), operator[]() +*/ + +/*! + \fn QChar QString::back() const + \since 5.10 + + Returns the last character in the string. + Same as \c{at(size() - 1)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa front(), at(), operator[]() +*/ + +/*! + \fn QCharRef QString::front() + \since 5.10 + + Returns a reference to the first character in the string. + Same as \c{operator[](0)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa back(), at(), operator[]() +*/ + +/*! + \fn QCharRef QString::back() + \since 5.10 + + Returns a reference to the last character in the string. + Same as \c{operator[](size() - 1)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa front(), at(), operator[]() +*/ + +/*! \fn void QString::truncate(int position) Truncates the string at the given \a position index. @@ -5271,8 +5825,6 @@ QString& QString::fill(QChar ch, int size) Note that no string is equal to \a s1 being 0. Equivalent to \c {s1 != 0 && compare(s1, s2) == 0}. - - \sa QString::compare() */ /*! @@ -5284,8 +5836,6 @@ QString& QString::fill(QChar ch, int size) For \a s1 != 0, this is equivalent to \c {compare(} \a s1, \a s2 \c {) != 0}. Note that no string is equal to \a s1 being 0. - - \sa QString::compare() */ /*! @@ -5300,8 +5850,6 @@ QString& QString::fill(QChar ch, int size) of the characters and is very fast, but is not what a human would expect. Consider sorting user-interface strings using the QString::localeAwareCompare() function. - - \sa QString::compare() */ /*! @@ -5316,8 +5864,6 @@ QString& QString::fill(QChar ch, int size) of the characters and is very fast, but is not what a human would expect. Consider sorting user-interface strings with QString::localeAwareCompare(). - - \sa QString::compare() */ /*! @@ -5331,8 +5877,6 @@ QString& QString::fill(QChar ch, int size) of the characters and is very fast, but is not what a human would expect. Consider sorting user-interface strings using the QString::localeAwareCompare() function. - - \sa QString::compare() */ /*! @@ -5449,9 +5993,7 @@ QString& QString::fill(QChar ch, int size) */ int QString::compare(const QString &other, Qt::CaseSensitivity cs) const Q_DECL_NOTHROW { - if (cs == Qt::CaseSensitive) - return ucstrcmp(constData(), length(), other.constData(), other.length()); - return ucstricmp(d->data(), d->data() + d->size, other.d->data(), other.d->data() + other.d->size); + return qt_compare_strings(*this, other, cs); } /*! @@ -5461,11 +6003,11 @@ int QString::compare(const QString &other, Qt::CaseSensitivity cs) const Q_DECL_ int QString::compare_helper(const QChar *data1, int length1, const QChar *data2, int length2, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { - if (cs == Qt::CaseSensitive) - return ucstrcmp(data1, length1, data2, length2); - const ushort *s1 = reinterpret_cast<const ushort *>(data1); - const ushort *s2 = reinterpret_cast<const ushort *>(data2); - return ucstricmp(s1, s1 + length1, s2, s2 + length2); + Q_ASSERT(length1 >= 0); + Q_ASSERT(length2 >= 0); + Q_ASSERT(data1 || length1 == 0); + Q_ASSERT(data2 || length2 == 0); + return qt_compare_strings(QStringView(data1, length1), QStringView(data2, length2), cs); } /*! @@ -5476,7 +6018,7 @@ int QString::compare_helper(const QChar *data1, int length1, const QChar *data2, */ int QString::compare(QLatin1String other, Qt::CaseSensitivity cs) const Q_DECL_NOTHROW { - return compare_helper(unicode(), length(), other, cs); + return qt_compare_strings(*this, other, cs); } /*! @@ -5495,6 +6037,8 @@ int QString::compare(QLatin1String other, Qt::CaseSensitivity cs) const Q_DECL_N int QString::compare_helper(const QChar *data1, int length1, const char *data2, int length2, Qt::CaseSensitivity cs) { + Q_ASSERT(length1 >= 0); + Q_ASSERT(data1 || length1 == 0); if (!data2) return length1; if (Q_UNLIKELY(length2 < 0)) @@ -5503,7 +6047,7 @@ int QString::compare_helper(const QChar *data1, int length1, const char *data2, QVarLengthArray<ushort> s2(length2); const auto beg = reinterpret_cast<QChar *>(s2.data()); const auto end = QUtf8::convertToUnicode(beg, data2, length2); - return compare_helper(data1, length1, beg, end - beg, cs); + return qt_compare_strings(QStringView(data1, length1), QStringView(beg, end - beg), cs); } /*! @@ -5518,18 +6062,9 @@ int QString::compare_helper(const QChar *data1, int length1, const char *data2, int QString::compare_helper(const QChar *data1, int length1, QLatin1String s2, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { - const ushort *uc = reinterpret_cast<const ushort *>(data1); - const ushort *uce = uc + length1; - const uchar *c = (const uchar *)s2.latin1(); - - if (!c) - return length1; - - if (cs == Qt::CaseSensitive) { - return ucstrcmp(data1, length1, c, s2.size()); - } else { - return ucstricmp(uc, uce, c, c + s2.size()); - } + Q_ASSERT(length1 >= 0); + Q_ASSERT(data1 || length1 == 0); + return qt_compare_strings(QStringView(data1, length1), s2, cs); } /*! @@ -5615,9 +6150,15 @@ Q_GLOBAL_STATIC(QThreadStorage<QCollator>, defaultCollator) int QString::localeAwareCompare_helper(const QChar *data1, int length1, const QChar *data2, int length2) { + Q_ASSERT(length1 >= 0); + Q_ASSERT(data1 || length1 == 0); + Q_ASSERT(length2 >= 0); + Q_ASSERT(data2 || length2 == 0); + // do the right thing for null and empty if (length1 == 0 || length2 == 0) - return ucstrcmp(data1, length1, data2, length2); + return qt_compare_strings(QStringView(data1, length1), QStringView(data2, length2), + Qt::CaseSensitive); #if defined(Q_OS_WIN) # ifndef Q_OS_WINRT @@ -5658,10 +6199,12 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, // declared in <string.h> int delta = strcoll(toLocal8Bit_helper(data1, length1).constData(), toLocal8Bit_helper(data2, length2).constData()); if (delta == 0) - delta = ucstrcmp(data1, length1, data2, length2); + delta = qt_compare_strings(QStringView(data1, length1), QStringView(data2, length2), + Qt::CaseSensitive); return delta; #else - return ucstrcmp(data1, length1, data2, length2); + return qt_compare_strings(QStringView(data1, length1), QStringView(data2, length2), + Qt::CaseSensitive); #endif } @@ -6070,7 +6613,7 @@ static LengthMod parse_length_modifier(const char * &c) Q_DECL_NOTHROW } /*! - \fn QString::vasprintf(const char *cformat, va_list ap) + \fn QString QString::vasprintf(const char *cformat, va_list ap) \since 5.5 Equivalent method to asprintf(), but takes a va_list \a ap @@ -6361,7 +6904,7 @@ qlonglong QString::toIntegral_helper(const QChar *data, int len, bool *ok, int b } #endif - return QLocaleData::c()->stringToLongLong(data, len, base, ok, QLocale::RejectGroupSeparator); + return QLocaleData::c()->stringToLongLong(QStringView(data, len), base, ok, QLocale::RejectGroupSeparator); } @@ -6401,7 +6944,7 @@ qulonglong QString::toIntegral_helper(const QChar *data, uint len, bool *ok, int } #endif - return QLocaleData::c()->stringToUnsLongLong(data, len, base, ok, + return QLocaleData::c()->stringToUnsLongLong(QStringView(data, len), base, ok, QLocale::RejectGroupSeparator); } @@ -6603,7 +7146,7 @@ ushort QString::toUShort(bool *ok, int base) const double QString::toDouble(bool *ok) const { - return QLocaleData::c()->stringToDouble(constData(), size(), ok, QLocale::RejectGroupSeparator); + return QLocaleData::c()->stringToDouble(*this, ok, QLocale::RejectGroupSeparator); } /*! @@ -7261,10 +7804,10 @@ struct ArgEscapeData int escape_len; // total length of escape sequences which will be replaced }; -static ArgEscapeData findArgEscapes(const QString &s) +static ArgEscapeData findArgEscapes(QStringView s) { - const QChar *uc_begin = s.unicode(); - const QChar *uc_end = uc_begin + s.length(); + const QChar *uc_begin = s.begin(); + const QChar *uc_end = s.end(); ArgEscapeData d; @@ -7323,11 +7866,11 @@ static ArgEscapeData findArgEscapes(const QString &s) return d; } -static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int field_width, - const QString &arg, const QString &larg, QChar fillChar = QLatin1Char(' ')) +static QString replaceArgEscapes(QStringView s, const ArgEscapeData &d, int field_width, + QStringView arg, QStringView larg, QChar fillChar) { - const QChar *uc_begin = s.unicode(); - const QChar *uc_end = uc_begin + s.length(); + const QChar *uc_begin = s.begin(); + const QChar *uc_end = s.end(); int abs_field_width = qAbs(field_width); int result_len = s.length() @@ -7391,11 +7934,11 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f } if (locale_arg) { - memcpy(rc, larg.unicode(), larg.length()*sizeof(QChar)); + memcpy(rc, larg.data(), larg.length()*sizeof(QChar)); rc += larg.length(); } else { - memcpy(rc, arg.unicode(), arg.length()*sizeof(QChar)); + memcpy(rc, arg.data(), arg.length()*sizeof(QChar)); rc += arg.length(); } @@ -7417,6 +7960,7 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f return result; } +#if QT_STRINGVIEW_LEVEL < 2 /*! Returns a copy of this string with the lowest numbered place marker replaced by string \a a, i.e., \c %1, \c %2, ..., \c %99. @@ -7448,17 +7992,86 @@ static QString replaceArgEscapes(const QString &s, const ArgEscapeData &d, int f */ QString QString::arg(const QString &a, int fieldWidth, QChar fillChar) const { + return arg(qToStringViewIgnoringNull(a), fieldWidth, fillChar); +} +#endif // QT_STRINGVIEW_LEVEL < 2 + +/*! + \overload + \since 5.10 + + Returns a copy of this string with the lowest-numbered place-marker + replaced by string \a a, i.e., \c %1, \c %2, ..., \c %99. + + \a fieldWidth specifies the minimum amount of space that \a a + shall occupy. If \a a requires less space than \a fieldWidth, it + is padded to \a fieldWidth with character \a fillChar. A positive + \a fieldWidth produces right-aligned text. A negative \a fieldWidth + produces left-aligned text. + + This example shows how we might create a \c status string for + reporting progress while processing a list of files: + + \snippet qstring/main.cpp 11-qstringview + + First, \c arg(i) replaces \c %1. Then \c arg(total) replaces \c + %2. Finally, \c arg(fileName) replaces \c %3. + + One advantage of using arg() over asprintf() is that the order of the + numbered place markers can change, if the application's strings are + translated into other languages, but each arg() will still replace + the lowest-numbered unreplaced place-marker, no matter where it + appears. Also, if place-marker \c %i appears more than once in the + string, arg() replaces all of them. + + If there is no unreplaced place-marker remaining, a warning message + is printed and the result is undefined. Place-marker numbers must be + in the range 1 to 99. +*/ +QString QString::arg(QStringView a, int fieldWidth, QChar fillChar) const +{ ArgEscapeData d = findArgEscapes(*this); - if (d.occurrences == 0) { - qWarning("QString::arg: Argument missing: %s, %s", toLocal8Bit().data(), - a.toLocal8Bit().data()); + if (Q_UNLIKELY(d.occurrences == 0)) { + qWarning("QString::arg: Argument missing: %ls, %ls", qUtf16Printable(*this), + qUtf16Printable(a.toString())); return *this; } return replaceArgEscapes(*this, d, fieldWidth, a, a, fillChar); } /*! + \overload + \since 5.10 + + Returns a copy of this string with the lowest-numbered place-marker + replaced by string \a a, i.e., \c %1, \c %2, ..., \c %99. + + \a fieldWidth specifies the minimum amount of space that \a a + shall occupy. If \a a requires less space than \a fieldWidth, it + is padded to \a fieldWidth with character \a fillChar. A positive + \a fieldWidth produces right-aligned text. A negative \a fieldWidth + produces left-aligned text. + + One advantage of using arg() over asprintf() is that the order of the + numbered place markers can change, if the application's strings are + translated into other languages, but each arg() will still replace + the lowest-numbered unreplaced place-marker, no matter where it + appears. Also, if place-marker \c %i appears more than once in the + string, arg() replaces all of them. + + If there is no unreplaced place-marker remaining, a warning message + is printed and the result is undefined. Place-marker numbers must be + in the range 1 to 99. +*/ +QString QString::arg(QLatin1String a, int fieldWidth, QChar fillChar) const +{ + QVarLengthArray<ushort> utf16(a.size()); + qt_from_latin1(utf16.data(), a.data(), a.size()); + return arg(QStringView(utf16.data(), utf16.size()), fieldWidth, fillChar); +} + +/*! \fn QString QString::arg(const QString& a1, const QString& a2) const \overload arg() @@ -8068,7 +8681,7 @@ bool QString::isSimpleText() const */ bool QString::isRightToLeft() const { - return QStringRef(this).isRightToLeft(); + return QtPrivate::isRightToLeft(QStringView(*this)); } /*! \fn QChar *QString::data() @@ -8147,6 +8760,15 @@ bool QString::isRightToLeft() const Appends the given \a ch character onto the end of this string. */ +/*! \fn void QString::shrink_to_fit() + \since 5.10 + + This function is provided for STL compatibility. It is + equivalent to squeeze(). + + \sa squeeze() +*/ + /*! \fn std::string QString::toStdString() const @@ -8324,6 +8946,83 @@ QString &QString::setRawData(const QChar *unicode, int size) \sa QString, QLatin1Char, {QStringLiteral()}{QStringLiteral} */ +/*! + \typedef QLatin1String::value_type + \since 5.10 + + Alias for \c{const char}. Provided for compatibility with the STL. +*/ + +/*! + \typedef QLatin1String::difference_type + \since 5.10 + + Alias for \c{int}. Provided for compatibility with the STL. +*/ + +/*! + \typedef QLatin1String::size_type + \since 5.10 + + Alias for \c{int}. Provided for compatibility with the STL. +*/ + +/*! + \typedef QLatin1String::reference + \since 5.10 + + Alias for \c{value_type &}. Provided for compatibility with the STL. +*/ + +/*! + \typedef QLatin1String::const_reference + \since 5.11 + + Alias for \c{reference}. Provided for compatibility with the STL. +*/ + +/*! + \typedef QLatin1String::iterator + \since 5.10 + + This typedef provides an STL-style const iterator for QLatin1String. + + QLatin1String does not support mutable iterators, so this is the same + as const_iterator. + + \sa const_iterator, reverse_iterator +*/ + +/*! + \typedef QLatin1String::const_iterator + \since 5.10 + + This typedef provides an STL-style const iterator for QLatin1String. + + \sa iterator, const_reverse_iterator +*/ + +/*! + \typedef QLatin1String::reverse_iterator + \since 5.10 + + This typedef provides an STL-style const reverse iterator for QLatin1String. + + QLatin1String does not support mutable reverse iterators, so this is the + same as const_reverse_iterator. + + \sa const_reverse_iterator, iterator +*/ + +/*! + \typedef QLatin1String::const_reverse_iterator + \since 5.10 + + This typedef provides an STL-style const reverse iterator for QLatin1String. + + \sa reverse_iterator, const_iterator +*/ + /*! \fn QLatin1String::QLatin1String() \since 5.6 @@ -8352,6 +9051,24 @@ QString &QString::setRawData(const QChar *unicode, int size) \sa latin1() */ +/*! + \fn QLatin1String::QLatin1String(const char *first, const char *last) + \since 5.10 + + Constructs a QLatin1String object that stores \a first with length + (\a last - \a first). + + The range \c{[first,last)} must remain valid for the lifetime of + this Latin-1 string object. + + Passing \c nullptr as \a first is safe if \a last is \c nullptr, + too, and results in a null Latin-1 string. + + The behavior is undefined if \a last precedes \a first, \a first + is \c nullptr and \a last is not, or if \c{last - first > + INT_MAX}. +*/ + /*! \fn QLatin1String::QLatin1String(const QByteArray &str) Constructs a QLatin1String object that stores \a str. @@ -8378,6 +9095,24 @@ QString &QString::setRawData(const QChar *unicode, int size) Returns the size of the Latin-1 string stored in this object. */ +/*! \fn bool QLatin1String::isNull() const + \since 5.10 + + Returns whether the Latin-1 string stored in this object is null + (\c{data() == nullptr}) or not. + + \sa isEmpty(), data() +*/ + +/*! \fn bool QLatin1String::isEmpty() const + \since 5.10 + + Returns whether the Latin-1 string stored in this object is empty + (\c{size() == 0}) or not. + + \sa isNull(), size() +*/ + /*! \fn QLatin1Char QLatin1String::at(int pos) const \since 5.8 @@ -8400,6 +9135,167 @@ QString &QString::setRawData(const QChar *unicode, int size) \sa at() */ +/*! + \fn QLatin1Char QLatin1String::front() const + \since 5.10 + + Returns the first character in the string. + Same as \c{at(0)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa back(), at(), operator[]() +*/ + +/*! + \fn QLatin1Char QLatin1String::back() const + \since 5.10 + + Returns the last character in the string. + Same as \c{at(size() - 1)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa front(), at(), operator[]() +*/ + +/*! + \fn bool QLatin1String::startsWith(QStringView str, Qt::CaseSensitivity cs) const + \since 5.10 + \fn bool QLatin1String::startsWith(QLatin1String l1, Qt::CaseSensitivity cs) const + \since 5.10 + \fn bool QLatin1String::startsWith(QChar ch) const + \since 5.10 + \fn bool QLatin1String::startsWith(QChar ch, Qt::CaseSensitivity cs) const + \since 5.10 + + Returns \c true if this Latin-1 string starts with string-view \a str, + Latin-1 string \a l1, or character \a ch, respectively; + otherwise returns \c false. + + If \a cs is Qt::CaseSensitive (the default), the search is case-sensitive; + otherwise the search is case-insensitive. + + \sa endsWith() +*/ + +/*! + \fn bool QLatin1String::endsWith(QStringView str, Qt::CaseSensitivity cs) const + \since 5.10 + \fn bool QLatin1String::endsWith(QLatin1String l1, Qt::CaseSensitivity cs) const + \since 5.10 + \fn bool QLatin1String::endsWith(QChar ch) const + \since 5.10 + \fn bool QLatin1String::endsWith(QChar ch, Qt::CaseSensitivity cs) const + \since 5.10 + + Returns \c true if this Latin-1 string ends with string-view \a str, + Latin-1 string \a l1, or character \a ch, respectively; + otherwise returns \c false. + + If \a cs is Qt::CaseSensitive (the default), the search is case-sensitive; + otherwise the search is case-insensitive. + + \sa startsWith() +*/ + +/*! + \fn QLatin1String::const_iterator QLatin1String::begin() const + \since 5.10 + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first character in + the string. + + This function is provided for STL compatibility. + + \sa end(), cbegin(), rbegin(), data() +*/ + +/*! + \fn QLatin1String::const_iterator QLatin1String::cbegin() const + \since 5.10 + + Same as begin(). + + This function is provided for STL compatibility. + + \sa cend(), begin(), crbegin(), data() +*/ + +/*! + \fn QLatin1String::const_iterator QLatin1String::end() const + \since 5.10 + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + character after the last character in the list. + + This function is provided for STL compatibility. + + \sa begin(), cend(), rend() +*/ + +/*! \fn QLatin1String::const_iterator QLatin1String::cend() const + \since 5.10 + + Same as end(). + + This function is provided for STL compatibility. + + \sa cbegin(), end(), crend() +*/ + +/*! + \fn QLatin1String::const_reverse_iterator QLatin1String::rbegin() const + \since 5.10 + + Returns a const \l{STL-style iterators}{STL-style} reverse iterator pointing to the first + character in the string, in reverse order. + + This function is provided for STL compatibility. + + \sa rend(), crbegin(), begin() +*/ + +/*! + \fn QLatin1String::const_reverse_iterator QLatin1String::crbegin() const + \since 5.10 + + Same as rbegin(). + + This function is provided for STL compatibility. + + \sa crend(), rbegin(), cbegin() +*/ + +/*! + \fn QLatin1String::const_reverse_iterator QLatin1String::rend() const + \since 5.10 + + Returns a \l{STL-style iterators}{STL-style} reverse iterator pointing to one past + the last character in the string, in reverse order. + + This function is provided for STL compatibility. + + \sa rbegin(), crend(), end() +*/ + +/*! + \fn QLatin1String::const_reverse_iterator QLatin1String::crend() const + \since 5.10 + + Same as rend(). + + This function is provided for STL compatibility. + + \sa crbegin(), rend(), cend() +*/ + /*! \fn QLatin1String QLatin1String::mid(int start) const \since 5.8 @@ -8409,7 +9305,7 @@ QString &QString::setRawData(const QChar *unicode, int size) \note This function performs no error checking. The behavior is undefined when \a start < 0 or \a start > size(). - \sa left(), right() + \sa left(), right(), chopped(), chop(), truncate() */ /*! \fn QLatin1String QLatin1String::mid(int start, int length) const @@ -8423,7 +9319,7 @@ QString &QString::setRawData(const QChar *unicode, int size) The behavior is undefined when \a start < 0, \a length < 0, or \a start + \a length > size(). - \sa left(), right() + \sa left(), right(), chopped(), chop(), truncate() */ /*! \fn QLatin1String QLatin1String::left(int length) const @@ -8435,7 +9331,7 @@ QString &QString::setRawData(const QChar *unicode, int size) \note This function performs no error checking. The behavior is undefined when \a length < 0 or \a length > size(). - \sa mid(), right() + \sa mid(), right(), chopped(), chop(), truncate() */ /*! \fn QLatin1String QLatin1String::right(int length) const @@ -8447,7 +9343,58 @@ QString &QString::setRawData(const QChar *unicode, int size) \note This function performs no error checking. The behavior is undefined when \a length < 0 or \a length > size(). - \sa mid(), left() + \sa mid(), left(), chopped(), chop(), truncate() +*/ + +/*! + \fn QLatin1String QLatin1String::chopped(int length) const + \since 5.10 + + Returns the substring of length size() - \a length starting at the + beginning of this object. + + Same as \c{left(size() - length)}. + + \note The behavior is undefined when \a length < 0 or \a length > size(). + + \sa mid(), left(), right(), chop(), truncate() +*/ + +/*! + \fn void QLatin1String::truncate(int length) + \since 5.10 + + Truncates this string to length \a length. + + Same as \c{*this = left(length)}. + + \note The behavior is undefined when \a length < 0 or \a length > size(). + + \sa mid(), left(), right(), chopped(), chop() +*/ + +/*! + \fn void QLatin1String::chop(int length) + \since 5.10 + + Truncates this string by \a length characters. + + Same as \c{*this = left(size() - length)}. + + \note The behavior is undefined when \a length < 0 or \a length > size(). + + \sa mid(), left(), right(), chopped(), truncate() +*/ + +/*! + \fn QLatin1String QLatin1String::trimmed() const + \since 5.10 + + Strips leading and trailing whitespace and returns the result. + + Whitespace means any character for which QChar::isSpace() returns + \c true. This includes the ASCII characters '\\t', '\\n', '\\v', + '\\f', '\\r', and ' '. */ /*! \fn bool QLatin1String::operator==(const QString &other) const @@ -9145,8 +10092,8 @@ QString QStringRef::toString() const { returns \c false. */ bool operator==(const QStringRef &s1,const QStringRef &s2) Q_DECL_NOTHROW -{ return (s1.size() == s2.size() && - qMemEquals((const ushort *)s1.unicode(), (const ushort *)s2.unicode(), s1.size())); +{ + return s1.size() == s2.size() && qt_compare_strings(s1, s2, Qt::CaseSensitive) == 0; } /*! \relates QStringRef @@ -9155,8 +10102,8 @@ bool operator==(const QStringRef &s1,const QStringRef &s2) Q_DECL_NOTHROW returns \c false. */ bool operator==(const QString &s1,const QStringRef &s2) Q_DECL_NOTHROW -{ return (s1.size() == s2.size() && - qMemEquals((const ushort *)s1.unicode(), (const ushort *)s2.unicode(), s1.size())); +{ + return s1.size() == s2.size() && qt_compare_strings(s1, s2, Qt::CaseSensitive) == 0; } /*! \relates QStringRef @@ -9169,10 +10116,7 @@ bool operator==(QLatin1String s1, const QStringRef &s2) Q_DECL_NOTHROW if (s1.size() != s2.size()) return false; - const uchar *c = reinterpret_cast<const uchar *>(s1.latin1()); - if (!c) - return s2.isEmpty(); - return ucstrncmp(s2.unicode(), c, s2.size()) == 0; + return qt_compare_strings(s2, s1, Qt::CaseSensitive) == 0; } /*! @@ -9188,7 +10132,7 @@ bool operator==(QLatin1String s1, const QStringRef &s2) Q_DECL_NOTHROW */ bool operator<(const QStringRef &s1,const QStringRef &s2) Q_DECL_NOTHROW { - return ucstrcmp(s1.constData(), s1.length(), s2.constData(), s2.length()) < 0; + return qt_compare_strings(s1, s2, Qt::CaseSensitive) < 0; } /*!\fn bool operator<=(const QStringRef &s1,const QStringRef &s2) @@ -9255,6 +10199,36 @@ bool operator<(const QStringRef &s1,const QStringRef &s2) Q_DECL_NOTHROW */ /*! + \fn QChar QStringRef::front() const + \since 5.10 + + Returns the first character in the string. + Same as \c{at(0)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa back(), at(), operator[]() +*/ + +/*! + \fn QChar QStringRef::back() const + \since 5.10 + + Returns the last character in the string. + Same as \c{at(size() - 1)}. + + This function is provided for STL compatibility. + + \warning Calling this function on an empty string constitutes + undefined behavior. + + \sa front(), at(), operator[]() +*/ + +/*! \fn void QStringRef::clear() Clears the contents of the string reference by making it null and empty. @@ -9460,8 +10434,6 @@ QStringRef QStringRef::appendTo(QString *string) const otherwise the comparison is case insensitive. Equivalent to \c {compare(*this, other, cs)}. - - \sa QString::compare() */ /*! @@ -9477,8 +10449,6 @@ QStringRef QStringRef::appendTo(QString *string) const otherwise the comparison is case insensitive. Equivalent to \c {compare(*this, other, cs)}. - - \sa QString::compare() */ /*! @@ -9494,8 +10464,6 @@ QStringRef QStringRef::appendTo(QString *string) const otherwise the comparison is case insensitive. Equivalent to \c {compare(*this, other, cs)}. - - \sa QString::compare() */ /*! @@ -9512,8 +10480,6 @@ QStringRef QStringRef::appendTo(QString *string) const otherwise the comparison is case insensitive. Equivalent to \c {compare(*this, other, cs)}. - - \sa QString::compare() */ /*! @@ -9605,7 +10571,7 @@ QString &QString::append(const QStringRef &str) If \a n is greater than or equal to size(), or less than zero, a reference to the entire string is returned. - \sa right(), mid(), startsWith() + \sa right(), mid(), startsWith(), chopped(), chop(), truncate() */ QStringRef QStringRef::left(int n) const { @@ -9642,7 +10608,7 @@ QStringRef QString::leftRef(int n) const If \a n is greater than or equal to size(), or less than zero, a reference to the entire string is returned. - \sa left(), mid(), endsWith() + \sa left(), mid(), endsWith(), chopped(), chop(), truncate() */ QStringRef QStringRef::right(int n) const { @@ -9670,7 +10636,7 @@ QStringRef QString::rightRef(int n) const } /*! - \fn QStringRef::mid(int position, int n = -1) const + \fn QStringRef QStringRef::mid(int position, int n = -1) const \since 5.2 Returns a substring reference to \a n characters of this string, @@ -9684,7 +10650,7 @@ QStringRef QString::rightRef(int n) const function returns all characters from the specified \a position onwards. - \sa left(), right() + \sa left(), right(), chopped(), chop(), truncate() */ QStringRef QStringRef::mid(int pos, int n) const { @@ -9704,6 +10670,18 @@ QStringRef QStringRef::mid(int pos, int n) const } /*! + \fn QStringRef QStringRef::chopped(int len) const + \since 5.10 + + Returns a substring reference to the size() - \a len leftmost characters + of this string. + + \note The behavior is undefined if \a len is negative or greater than size(). + + \sa endsWith(), left(), right(), mid(), chop(), truncate() +*/ + +/*! \since 4.4 Returns a substring reference to \a n characters of this string, @@ -9980,8 +10958,23 @@ int QStringRef::count(const QStringRef &str, Qt::CaseSensitivity cs) const */ bool QStringRef::isRightToLeft() const { - const ushort *p = reinterpret_cast<const ushort*>(unicode()); - const ushort * const end = p + size(); + return QtPrivate::isRightToLeft(QStringView(unicode(), size())); +} + +/*! + \since 5.11 + \internal + \relates QStringView + + Returns \c true if the string is read right to left. + + \sa QString::isRightToLeft() +*/ +bool QtPrivate::isRightToLeft(QStringView string) +{ + const ushort *p = reinterpret_cast<const ushort*>(string.data()); + const ushort * const end = p + string.size(); + int isolateLevel = 0; while (p < end) { uint ucs4 = *p; if (QChar::isHighSurrogate(ucs4) && p < end - 1) { @@ -9993,10 +10986,23 @@ bool QStringRef::isRightToLeft() const } switch (QChar::direction(ucs4)) { + case QChar::DirRLI: + case QChar::DirLRI: + case QChar::DirFSI: + ++isolateLevel; + break; + case QChar::DirPDI: + if (isolateLevel) + --isolateLevel; + break; case QChar::DirL: + if (isolateLevel) + break; return false; case QChar::DirR: case QChar::DirAL: + if (isolateLevel) + break; return true; default: break; @@ -10019,8 +11025,7 @@ bool QStringRef::isRightToLeft() const */ bool QStringRef::startsWith(const QString &str, Qt::CaseSensitivity cs) const { - return qt_starts_with(isNull() ? 0 : unicode(), size(), - str.isNull() ? 0 : str.unicode(), str.size(), cs); + return qt_starts_with(*this, str, cs); } /*! @@ -10030,18 +11035,24 @@ bool QStringRef::startsWith(const QString &str, Qt::CaseSensitivity cs) const */ bool QStringRef::startsWith(QLatin1String str, Qt::CaseSensitivity cs) const { - return qt_starts_with(isNull() ? 0 : unicode(), size(), str, cs); + return qt_starts_with(*this, str, cs); } /*! + \fn bool QStringRef::startsWith(QStringView str, Qt::CaseSensitivity cs) const + \since 5.10 + \overload startsWith() + \sa QString::startsWith(), endsWith() +*/ + +/*! \since 4.8 \overload startsWith() \sa QString::startsWith(), endsWith() */ bool QStringRef::startsWith(const QStringRef &str, Qt::CaseSensitivity cs) const { - return qt_starts_with(isNull() ? 0 : unicode(), size(), - str.isNull() ? 0 : str.unicode(), str.size(), cs); + return qt_starts_with(*this, str, cs); } /*! @@ -10058,14 +11069,7 @@ bool QStringRef::startsWith(const QStringRef &str, Qt::CaseSensitivity cs) const */ bool QStringRef::startsWith(QChar ch, Qt::CaseSensitivity cs) const { - if (!isEmpty()) { - const ushort *data = reinterpret_cast<const ushort*>(unicode()); - return (cs == Qt::CaseSensitive - ? data[0] == ch - : foldCase(data[0]) == foldCase(ch.unicode())); - } else { - return false; - } + return qt_starts_with(*this, ch, cs); } /*! @@ -10080,8 +11084,7 @@ bool QStringRef::startsWith(QChar ch, Qt::CaseSensitivity cs) const */ bool QStringRef::endsWith(const QString &str, Qt::CaseSensitivity cs) const { - return qt_ends_with(isNull() ? 0 : unicode(), size(), - str.isNull() ? 0 : str.unicode(), str.size(), cs); + return qt_ends_with(*this, str, cs); } /*! @@ -10098,15 +11101,7 @@ bool QStringRef::endsWith(const QString &str, Qt::CaseSensitivity cs) const */ bool QStringRef::endsWith(QChar ch, Qt::CaseSensitivity cs) const { - if (!isEmpty()) { - const ushort *data = reinterpret_cast<const ushort*>(unicode()); - const int size = length(); - return (cs == Qt::CaseSensitive - ? data[size - 1] == ch - : foldCase(data[size - 1]) == foldCase(ch.unicode())); - } else { - return false; - } + return qt_ends_with(*this, ch, cs); } /*! @@ -10116,18 +11111,24 @@ bool QStringRef::endsWith(QChar ch, Qt::CaseSensitivity cs) const */ bool QStringRef::endsWith(QLatin1String str, Qt::CaseSensitivity cs) const { - return qt_ends_with(isNull() ? 0 : unicode(), size(), str, cs); + return qt_ends_with(*this, str, cs); } /*! + \fn bool QStringRef::endsWith(QStringView str, Qt::CaseSensitivity cs) const + \since 5.10 + \overload endsWith() + \sa QString::endsWith(), startsWith() +*/ + +/*! \since 4.8 \overload endsWith() \sa QString::endsWith(), endsWith() */ bool QStringRef::endsWith(const QStringRef &str, Qt::CaseSensitivity cs) const { - return qt_ends_with(isNull() ? 0 : unicode(), size(), - str.isNull() ? 0 : str.unicode(), str.size(), cs); + return qt_ends_with(*this, str, cs); } @@ -10262,101 +11263,150 @@ static inline int qt_find_latin1_string(const QChar *haystack, int size, reinterpret_cast<const QChar*>(s.constData()), len, cs); } -static inline bool qt_starts_with(const QChar *haystack, int haystackLen, - const QChar *needle, int needleLen, Qt::CaseSensitivity cs) +template <typename Haystack, typename Needle> +bool qt_starts_with_impl(Haystack haystack, Needle needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { - if (!haystack) - return !needle; + if (haystack.isNull()) + return needle.isNull(); // historical behavior, consider changing in ### Qt 6. + const auto haystackLen = haystack.size(); + const auto needleLen = needle.size(); if (haystackLen == 0) return needleLen == 0; if (needleLen > haystackLen) return false; - const ushort *h = reinterpret_cast<const ushort*>(haystack); - const ushort *n = reinterpret_cast<const ushort*>(needle); + return qt_compare_strings(haystack.left(needleLen), needle, cs) == 0; +} - if (cs == Qt::CaseSensitive) { - return qMemEquals(h, n, needleLen); - } else { - uint last = 0; - uint olast = 0; - for (int i = 0; i < needleLen; ++i) - if (foldCase(h[i], last) != foldCase(n[i], olast)) - return false; - } - return true; +static inline bool qt_starts_with(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs) +{ + return qt_starts_with_impl(haystack, needle, cs); } -static inline bool qt_starts_with(const QChar *haystack, int haystackLen, - QLatin1String needle, Qt::CaseSensitivity cs) +static inline bool qt_starts_with(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs) { - if (!haystack) - return !needle.latin1(); - if (haystackLen == 0) - return !needle.latin1() || *needle.latin1() == 0; - const int slen = needle.size(); - if (slen > haystackLen) - return false; - const ushort *data = reinterpret_cast<const ushort*>(haystack); - const uchar *latin = reinterpret_cast<const uchar*>(needle.latin1()); - if (cs == Qt::CaseSensitive) { - return ucstrncmp(haystack, latin, slen) == 0; - } else { - for (int i = 0; i < slen; ++i) - if (foldCase(data[i]) != foldCase((ushort)latin[i])) - return false; - } - return true; + return qt_starts_with_impl(haystack, needle, cs); } -static inline bool qt_ends_with(const QChar *haystack, int haystackLen, - const QChar *needle, int needleLen, Qt::CaseSensitivity cs) +static inline bool qt_starts_with(QStringView haystack, QChar needle, Qt::CaseSensitivity cs) { - if (!haystack) - return !needle; + return haystack.size() + && (cs == Qt::CaseSensitive ? haystack.front() == needle + : foldCase(haystack.front()) == foldCase(needle)); +} + +/*! + \fn bool QtPrivate::startsWith(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs) + \since 5.10 + \fn bool QtPrivate::startsWith(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs) + \since 5.10 + \fn bool QtPrivate::startsWith(QLatin1String haystack, QStringView needle, Qt::CaseSensitivity cs) + \since 5.10 + \fn bool QtPrivate::startsWith(QLatin1String haystack, QLatin1String needle, Qt::CaseSensitivity cs) + \since 5.10 + \internal + \relates QStringView + + Returns \c true if \a haystack starts with \a needle, + otherwise returns \c false. + + If \a cs is Qt::CaseSensitive (the default), the search is case-sensitive; + otherwise the search is case-insensitive. + + \sa QtPrivate::endsWith(), QString::endsWith(), QStringView::endsWith(), QLatin1String::endsWith() +*/ + +bool QtPrivate::startsWith(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_starts_with_impl(haystack, needle, cs); +} + +bool QtPrivate::startsWith(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_starts_with_impl(haystack, needle, cs); +} + +bool QtPrivate::startsWith(QLatin1String haystack, QStringView needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_starts_with_impl(haystack, needle, cs); +} + +bool QtPrivate::startsWith(QLatin1String haystack, QLatin1String needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_starts_with_impl(haystack, needle, cs); +} + +template <typename Haystack, typename Needle> +bool qt_ends_with_impl(Haystack haystack, Needle needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + if (haystack.isNull()) + return needle.isNull(); // historical behavior, consider changing in ### Qt 6. + const auto haystackLen = haystack.size(); + const auto needleLen = needle.size(); if (haystackLen == 0) return needleLen == 0; - const int pos = haystackLen - needleLen; - if (pos < 0) + if (haystackLen < needleLen) return false; - const ushort *h = reinterpret_cast<const ushort*>(haystack); - const ushort *n = reinterpret_cast<const ushort*>(needle); + return qt_compare_strings(haystack.right(needleLen), needle, cs) == 0; +} + +static inline bool qt_ends_with(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs) +{ + return qt_ends_with_impl(haystack, needle, cs); +} - if (cs == Qt::CaseSensitive) { - return qMemEquals(h + pos, n, needleLen); - } else { - uint last = 0; - uint olast = 0; - for (int i = 0; i < needleLen; i++) - if (foldCase(h[pos+i], last) != foldCase(n[i], olast)) - return false; - } - return true; +static inline bool qt_ends_with(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs) +{ + return qt_ends_with_impl(haystack, needle, cs); } +static inline bool qt_ends_with(QStringView haystack, QChar needle, Qt::CaseSensitivity cs) +{ + return haystack.size() + && (cs == Qt::CaseSensitive ? haystack.back() == needle + : foldCase(haystack.back()) == foldCase(needle)); +} -static inline bool qt_ends_with(const QChar *haystack, int haystackLen, - QLatin1String needle, Qt::CaseSensitivity cs) +/*! + \fn bool QtPrivate::endsWith(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs) + \since 5.10 + \fn bool QtPrivate::endsWith(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs) + \since 5.10 + \fn bool QtPrivate::endsWith(QLatin1String haystack, QStringView needle, Qt::CaseSensitivity cs) + \since 5.10 + \fn bool QtPrivate::endsWith(QLatin1String haystack, QLatin1String needle, Qt::CaseSensitivity cs) + \since 5.10 + \internal + \relates QStringView + + Returns \c true if \a haystack ends with \a needle, + otherwise returns \c false. + + If \a cs is Qt::CaseSensitive (the default), the search is case-sensitive; + otherwise the search is case-insensitive. + + \sa QtPrivate::startsWith(), QString::endsWith(), QStringView::endsWith(), QLatin1String::endsWith() +*/ + +bool QtPrivate::endsWith(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW { - if (!haystack) - return !needle.latin1(); - if (haystackLen == 0) - return !needle.latin1() || *needle.latin1() == 0; - const int slen = needle.size(); - int pos = haystackLen - slen; - if (pos < 0) - return false; - const uchar *latin = reinterpret_cast<const uchar*>(needle.latin1()); - const ushort *data = reinterpret_cast<const ushort*>(haystack); - if (cs == Qt::CaseSensitive) { - return ucstrncmp(haystack + pos, latin, slen) == 0; - } else { - for (int i = 0; i < slen; i++) - if (foldCase(data[pos+i]) != foldCase((ushort)latin[i])) - return false; - } - return true; + return qt_ends_with_impl(haystack, needle, cs); +} + +bool QtPrivate::endsWith(QStringView haystack, QLatin1String needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_ends_with_impl(haystack, needle, cs); +} + +bool QtPrivate::endsWith(QLatin1String haystack, QStringView needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_ends_with_impl(haystack, needle, cs); +} + +bool QtPrivate::endsWith(QLatin1String haystack, QLatin1String needle, Qt::CaseSensitivity cs) Q_DECL_NOTHROW +{ + return qt_ends_with_impl(haystack, needle, cs); } /*! @@ -10372,9 +11422,7 @@ 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()); + return qt_convert_to_latin1(*this); } /*! @@ -10411,14 +11459,7 @@ QByteArray QStringRef::toLatin1() const */ QByteArray QStringRef::toLocal8Bit() const { -#ifndef QT_NO_TEXTCODEC - if (!isNull()) { - QTextCodec *localeCodec = QTextCodec::codecForLocale(); - if (localeCodec) - return localeCodec->fromUnicode(unicode(), length()); - } -#endif // QT_NO_TEXTCODEC - return toLatin1(); + return qt_convert_to_local_8bit(*this); } /*! @@ -10433,10 +11474,7 @@ QByteArray QStringRef::toLocal8Bit() const */ QByteArray QStringRef::toUtf8() const { - if (isNull()) - return QByteArray(); - - return QUtf8::convertFromUnicode(constData(), length()); + return qt_convert_to_utf8(*this); } /*! @@ -10455,11 +11493,7 @@ QByteArray QStringRef::toUtf8() const */ QVector<uint> QStringRef::toUcs4() const { - QVector<uint> v(length()); - uint *a = v.data(); - int len = QString::toUcs4_helper(reinterpret_cast<const ushort *>(unicode()), length(), a); - v.resize(len); - return v; + return qt_convert_to_ucs4(*this); } /*! @@ -10483,8 +11517,6 @@ QStringRef QStringRef::trimmed() const QStringAlgorithms<const QStringRef>::trimmed_helper_positions(begin, end); if (begin == cbegin() && end == cend()) return *this; - if (begin == end) - return QStringRef(); int position = m_position + (begin - cbegin()); return QStringRef(m_string, position, end - begin); } @@ -10717,7 +11749,7 @@ ushort QStringRef::toUShort(bool *ok, int base) const double QStringRef::toDouble(bool *ok) const { - return QLocaleData::c()->stringToDouble(constData(), size(), ok, QLocale::RejectGroupSeparator); + return QLocaleData::c()->stringToDouble(*this, ok, QLocale::RejectGroupSeparator); } /*! |