diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 0375d682031..3aa86c49a9a 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -9490,6 +9490,22 @@ QString &QString::setRawData(const QChar *unicode, qsizetype size) \overload */ +/*! + \fn qsizetype QLatin1String::count(QStringView str, Qt::CaseSensitivity cs) const + \fn qsizetype QLatin1String::count(QLatin1String l1, Qt::CaseSensitivity cs) const + \fn qsizetype QLatin1String::count(QChar ch, Qt::CaseSensitivity cs) const + \since 6.4 + + Returns the number of (potentially overlapping) occurrences of the + string-view \a str, Latin-1 string \a l1, or character \a ch, + respectively, in this Latin-1 string. + + If \a cs is Qt::CaseSensitive (default), the search is + case sensitive; otherwise the search is case insensitive. + + \sa contains(), indexOf() +*/ + /*! \fn QLatin1String::const_iterator QLatin1String::begin() const \since 5.10 @@ -10380,6 +10396,75 @@ qsizetype QtPrivate::count(QStringView haystack, QChar ch, Qt::CaseSensitivity c return num; } +qsizetype QtPrivate::count(QLatin1String haystack, QLatin1String needle, Qt::CaseSensitivity cs) +{ + qsizetype num = 0; + qsizetype i = -1; + + // TODO: use Boyer-Moore searcher for case-insensitive search too + // when QTBUG-100236 is done + if (cs == Qt::CaseSensitive) { + QByteArrayMatcher matcher(needle); + while ((i = matcher.indexIn(haystack, i + 1)) != -1) + ++num; + } else { + while ((i = QtPrivate::findString(haystack, i + 1, needle, cs)) != -1) + ++num; + } + return num; +} + +qsizetype QtPrivate::count(QLatin1String haystack, QStringView needle, Qt::CaseSensitivity cs) +{ + if (haystack.size() < needle.size()) + return 0; + + if (!QtPrivate::isLatin1(needle)) // won't find non-L1 UTF-16 needles in a L1 haystack! + return 0; + + qsizetype num = 0; + qsizetype i = -1; + + // TODO: use Boyer-Moore searcher for case-insensitive search too + // when QTBUG-100236 is done + if (cs == Qt::CaseSensitive) { + QVarLengthArray s(needle.size()); + qt_to_latin1_unchecked(s.data(), needle.utf16(), needle.size()); + + QByteArrayMatcher matcher(s); + while ((i = matcher.indexIn(haystack, i + 1)) != -1) + ++num; + } else { + while ((i = QtPrivate::findString(haystack, i + 1, needle, cs)) != -1) + ++num; + } + return num; +} + +qsizetype QtPrivate::count(QLatin1String haystack, QChar needle, Qt::CaseSensitivity cs) noexcept +{ + // non-L1 needles cannot possibly match in L1-only haystacks + if (needle.unicode() > 0xff) + return 0; + + qsizetype num = 0; + if (cs == Qt::CaseSensitive) { + const char needleL1 = needle.toLatin1(); + for (char c : haystack) { + if (c == needleL1) + ++num; + } + } else { + auto toLower = [](char ch) { return latin1Lower[uchar(ch)]; }; + const uchar ch = toLower(needle.toLatin1()); + for (char c : haystack) { + if (toLower(c) == ch) + ++num; + } + } + return num; +} + template bool qt_starts_with_impl(Haystack haystack, Needle needle, Qt::CaseSensitivity cs) noexcept { diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index 55cf471d10c..db4e0cbd8a7 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -177,6 +177,13 @@ public: [[nodiscard]] qsizetype lastIndexOf(QChar c, qsizetype from, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return QtPrivate::lastIndexOf(*this, from, QStringView(&c, 1), cs); } + [[nodiscard]] qsizetype count(QStringView str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { return QtPrivate::count(*this, str, cs); } + [[nodiscard]] qsizetype count(QLatin1String str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const + { return QtPrivate::count(*this, str, cs); } + [[nodiscard]] qsizetype count(QChar ch, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { return QtPrivate::count(*this, ch, cs); } + [[nodiscard]] short toShort(bool *ok = nullptr, int base = 10) const { return QtPrivate::toIntegral(QByteArrayView(*this), ok, base); } [[nodiscard]] ushort toUShort(bool *ok = nullptr, int base = 10) const diff --git a/src/corelib/text/qstringalgorithms.h b/src/corelib/text/qstringalgorithms.h index 68d47b28934..1fa32c8bc2d 100644 --- a/src/corelib/text/qstringalgorithms.h +++ b/src/corelib/text/qstringalgorithms.h @@ -123,6 +123,9 @@ namespace QtPrivate { [[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype count(QStringView haystack, QChar needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; [[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype count(QStringView haystack, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype count(QLatin1String haystack, QLatin1String needle, Qt::CaseSensitivity cs = Qt::CaseSensitive); +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype count(QLatin1String haystack, QStringView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive); +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype count(QLatin1String haystack, QChar needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; #if QT_CONFIG(regularexpression) [[nodiscard]] Q_CORE_EXPORT qsizetype indexOf(QStringView haystack, diff --git a/tests/auto/corelib/text/qlatin1string/tst_qlatin1string.cpp b/tests/auto/corelib/text/qlatin1string/tst_qlatin1string.cpp index c1ca1f74c0e..aebd60ded22 100644 --- a/tests/auto/corelib/text/qlatin1string/tst_qlatin1string.cpp +++ b/tests/auto/corelib/text/qlatin1string/tst_qlatin1string.cpp @@ -55,6 +55,7 @@ private Q_SLOTS: void iterators(); void relationalOperators_data(); void relationalOperators(); + void count(); }; void tst_QLatin1String::constExpr() @@ -398,6 +399,36 @@ void tst_QLatin1String::relationalOperators() #undef CHECK } +void tst_QLatin1String::count() +{ + QLatin1String a("ABCDEFGHIEfGEFG"); + QCOMPARE(a.size(), 15); + QCOMPARE(a.count('A'), 1); + QCOMPARE(a.count('Z'), 0); + QCOMPARE(a.count('E'), 3); + QCOMPARE(a.count('F'), 2); + QCOMPARE(a.count('F', Qt::CaseInsensitive), 3); + QCOMPARE(a.count(QLatin1String("FG")), 2); + QCOMPARE(a.count(QLatin1String("FG"), Qt::CaseInsensitive), 3); + QCOMPARE(a.count(QLatin1String(), Qt::CaseInsensitive), 16); + QCOMPARE(a.count(QLatin1String(""), Qt::CaseInsensitive), 16); + + QLatin1String nullStr; + QCOMPARE(nullStr.count('A'), 0); + QCOMPARE(nullStr.count(QLatin1String("AB")), 0); + QCOMPARE(nullStr.count(QLatin1String()), 1); + QCOMPARE(nullStr.count(QLatin1String("")), 1); + + QLatin1String emptyStr(""); + QCOMPARE(emptyStr.count('A'), 0); + QCOMPARE(emptyStr.count(QLatin1String("AB")), 0); + QCOMPARE(emptyStr.count(QLatin1String()), 1); + QCOMPARE(emptyStr.count(QLatin1String("")), 1); + + using namespace Qt::Literals::StringLiterals; + QCOMPARE("a\0b"_L1.count(QChar::SpecialCharacter::LineSeparator), 0); +} + QTEST_APPLESS_MAIN(tst_QLatin1String) #include "tst_qlatin1string.moc" diff --git a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp index 56bb74d3c7b..32d5e014bde 100644 --- a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -748,6 +748,19 @@ private Q_SLOTS: void count_QStringView_char16_t_data() { count_data(); } void count_QStringView_char16_t() { count_impl(); } + void count_QLatin1String_QString_data() { count_data(); } + void count_QLatin1String_QString() { count_impl(); } + void count_QLatin1String_QLatin1String_data() { count_data(); } + void count_QLatin1String_QLatin1String() { count_impl(); } + void count_QLatin1String_QStringView_data() { count_data(); } + void count_QLatin1String_QStringView() { count_impl(); } + void count_QLatin1String_QChar_data() { count_data(); } + void count_QLatin1String_QChar() { count_impl(); } + void count_QLatin1String_char16_t_data() { count_data(); } + void count_QLatin1String_char16_t() { count_impl(); } + void count_QLatin1String_QLatin1Char_data() { count_data(); } + void count_QLatin1String_QLatin1Char() { count_impl(); } + // // UTF-16-only checks: //