From b977ae371a753a82e1d0bb32c5b62099da663721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98ystein=20Heskestad?= Date: Tue, 22 Nov 2022 17:48:45 +0100 Subject: [PATCH] Add In-place utf-8 case-insensitive comparisons Also add optimizations for more string comparisons and add tests and benchmarks. [ChangeLog][QtCore][QString] Added utf-8 case-insensitive comparisons Fixes: QTBUG-100235 Change-Id: I7c0809c6d80c00e9a5d0e8ac3ebb045cf7004a30 Reviewed-by: Thiago Macieira --- src/corelib/text/qstring.cpp | 13 +- src/corelib/text/qstring.h | 13 + src/corelib/text/qstringconverter.cpp | 65 ++++- src/corelib/text/qstringconverter_p.h | 8 +- src/corelib/text/qstringview.h | 16 ++ src/corelib/text/qutf8stringview.h | 11 + .../tst_qstringapisymmetry.cpp | 17 ++ tests/benchmarks/corelib/text/CMakeLists.txt | 1 + .../text/qutf8stringview/CMakeLists.txt | 14 + .../tst_bench_qutf8stringview.cpp | 263 ++++++++++++++++++ 10 files changed, 406 insertions(+), 15 deletions(-) create mode 100644 tests/benchmarks/corelib/text/qutf8stringview/CMakeLists.txt create mode 100644 tests/benchmarks/corelib/text/qutf8stringview/tst_bench_qutf8stringview.cpp diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index ca949b9b320..3ae6404ed4f 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -1482,8 +1482,7 @@ bool QtPrivate::equalStrings(QStringView lhs, QBasicUtf8StringView rhs) n bool QtPrivate::equalStrings(QLatin1StringView lhs, QBasicUtf8StringView rhs) noexcept { - QString r = rhs.toString(); - return QtPrivate::equalStrings(lhs, r); // ### optimize! + return QUtf8::compareUtf8(QByteArrayView(rhs), lhs) == 0; } bool QtPrivate::equalStrings(QBasicUtf8StringView lhs, QLatin1StringView rhs) noexcept @@ -1612,7 +1611,7 @@ int QtPrivate::compareStrings(QLatin1StringView lhs, QLatin1StringView rhs, Qt:: */ int QtPrivate::compareStrings(QLatin1StringView lhs, QBasicUtf8StringView rhs, Qt::CaseSensitivity cs) noexcept { - return compareStrings(lhs, rhs.toString(), cs); // ### optimize! + return -QUtf8::compareUtf8(QByteArrayView(rhs), lhs, cs); } /*! @@ -1647,13 +1646,7 @@ int QtPrivate::compareStrings(QBasicUtf8StringView lhs, QLatin1StringView */ int QtPrivate::compareStrings(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs, Qt::CaseSensitivity cs) noexcept { - if (lhs.isEmpty()) - return lencmp(0, rhs.size()); - if (cs == Qt::CaseInsensitive) - return compareStrings(lhs.toString(), rhs.toString(), cs); // ### optimize! - const auto l = std::min(lhs.size(), rhs.size()); - int r = memcmp(lhs.data(), rhs.data(), l); - return r ? r : lencmp(lhs.size(), rhs.size()); + return QUtf8::compareUtf8(QByteArrayView(lhs), QByteArrayView(rhs), cs); } int QAnyStringView::compare(QAnyStringView lhs, QAnyStringView rhs, Qt::CaseSensitivity cs) noexcept diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index d8d8cd34254..32847e30f60 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -119,6 +119,12 @@ public: { return isEmpty() ? -1 : front() == c ? int(size() > 1) : uchar(m_data[0]) - c.unicode(); } [[nodiscard]] int compare(QChar c, Qt::CaseSensitivity cs) const noexcept { return QtPrivate::compareStrings(*this, QStringView(&c, 1), cs); } + template + [[nodiscard]] int compare(QBasicUtf8StringView other, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { + return QtPrivate::compareStrings(*this, other, cs); + } [[nodiscard]] bool startsWith(QStringView s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return QtPrivate::startsWith(*this, s, cs); } @@ -1230,6 +1236,13 @@ QString QBasicUtf8StringView::toString() const return QString::fromUtf8(data(), size()); } +template +[[nodiscard]] int QBasicUtf8StringView::compare(QLatin1StringView other, + Qt::CaseSensitivity cs) const noexcept +{ + return QtPrivate::compareStrings(*this, other, cs); +} + // // QAnyStringView inline members that require QString: // diff --git a/src/corelib/text/qstringconverter.cpp b/src/corelib/text/qstringconverter.cpp index bdba20de3ac..04bd8e97338 100644 --- a/src/corelib/text/qstringconverter.cpp +++ b/src/corelib/text/qstringconverter.cpp @@ -820,7 +820,7 @@ QUtf8::ValidUtf8Result QUtf8::isValidUtf8(QByteArrayView in) return { true, isValidAscii }; } -int QUtf8::compareUtf8(QByteArrayView utf8, QStringView utf16) noexcept +int QUtf8::compareUtf8(QByteArrayView utf8, QStringView utf16, Qt::CaseSensitivity cs) noexcept { auto src1 = reinterpret_cast(utf8.data()); auto end1 = src1 + utf8.size(); @@ -847,7 +847,10 @@ int QUtf8::compareUtf8(QByteArrayView utf8, QStringView utf16) noexcept if (QChar::isHighSurrogate(uc2) && src2 < end2 && QChar::isLowSurrogate(*src2)) uc2 = QChar::surrogateToUcs4(uc2, *src2++); } - + if (cs == Qt::CaseInsensitive) { + uc1 = QChar::toCaseFolded(uc1); + uc2 = QChar::toCaseFolded(uc2); + } if (uc1 != uc2) return int(uc1) - int(uc2); } @@ -857,7 +860,7 @@ int QUtf8::compareUtf8(QByteArrayView utf8, QStringView utf16) noexcept return (end1 > src1) - int(end2 > src2); } -int QUtf8::compareUtf8(QByteArrayView utf8, QLatin1StringView s) +int QUtf8::compareUtf8(QByteArrayView utf8, QLatin1StringView s, Qt::CaseSensitivity cs) { char32_t uc1 = QChar::Null; auto src1 = reinterpret_cast(utf8.data()); @@ -875,6 +878,62 @@ int QUtf8::compareUtf8(QByteArrayView utf8, QLatin1StringView s) } char32_t uc2 = *src2++; + if (cs == Qt::CaseInsensitive) { + uc1 = QChar::toCaseFolded(uc1); + uc2 = QChar::toCaseFolded(uc2); + } + if (uc1 != uc2) + return int(uc1) - int(uc2); + } + + // the shorter string sorts first + return (end1 > src1) - (end2 > src2); +} + +static inline int lencmp(qsizetype lhs, qsizetype rhs) noexcept +{ + return lhs == rhs ? 0 : + lhs > rhs ? 1 : + /* else */ -1 ; +} + +int QUtf8::compareUtf8(QByteArrayView lhs, QByteArrayView rhs, Qt::CaseSensitivity cs) noexcept +{ + if (lhs.isEmpty()) + return lencmp(0, rhs.size()); + + if (cs == Qt::CaseSensitive) { + const auto l = std::min(lhs.size(), rhs.size()); + int r = memcmp(lhs.data(), rhs.data(), l); + return r ? r : lencmp(lhs.size(), rhs.size()); + } + + char32_t uc1 = QChar::Null; + auto src1 = reinterpret_cast(lhs.data()); + auto end1 = src1 + lhs.size(); + char32_t uc2 = QChar::Null; + auto src2 = reinterpret_cast(rhs.data()); + auto end2 = src2 + rhs.size(); + + while (src1 < end1 && src2 < end2) { + uchar b = *src1++; + char32_t *output = &uc1; + int res = QUtf8Functions::fromUtf8(b, output, src1, end1); + if (res < 0) { + // decoding error + uc1 = QChar::ReplacementCharacter; + } + + b = *src2++; + output = &uc2; + res = QUtf8Functions::fromUtf8(b, output, src2, end2); + if (res < 0) { + // decoding error + uc2 = QChar::ReplacementCharacter; + } + + uc1 = QChar::toCaseFolded(uc1); + uc2 = QChar::toCaseFolded(uc2); if (uc1 != uc2) return int(uc1) - int(uc2); } diff --git a/src/corelib/text/qstringconverter_p.h b/src/corelib/text/qstringconverter_p.h index 26be8b713c1..95d1eea072b 100644 --- a/src/corelib/text/qstringconverter_p.h +++ b/src/corelib/text/qstringconverter_p.h @@ -276,8 +276,12 @@ struct QUtf8 bool isValidAscii; }; static ValidUtf8Result isValidUtf8(QByteArrayView in); - static int compareUtf8(QByteArrayView utf8, QStringView utf16) noexcept; - static int compareUtf8(QByteArrayView utf8, QLatin1StringView s); + static int compareUtf8(QByteArrayView utf8, QStringView utf16, + Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; + static int compareUtf8(QByteArrayView utf8, QLatin1StringView s, + Qt::CaseSensitivity cs = Qt::CaseSensitive); + static int compareUtf8(QByteArrayView lhs, QByteArrayView rhs, + Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; }; struct QUtf16 diff --git a/src/corelib/text/qstringview.h b/src/corelib/text/qstringview.h index 2015927f190..95b089d565d 100644 --- a/src/corelib/text/qstringview.h +++ b/src/corelib/text/qstringview.h @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -254,6 +255,12 @@ public: [[nodiscard]] int compare(QStringView other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept { return QtPrivate::compareStrings(*this, other, cs); } [[nodiscard]] inline int compare(QLatin1StringView other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + template + [[nodiscard]] int compare(QBasicUtf8StringView other, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { + return QtPrivate::compareStrings(*this, other, cs); + } [[nodiscard]] constexpr int compare(QChar c) const noexcept { return size() >= 1 ? compare_single_char_helper(*utf16() - c.unicode()) : -1; } [[nodiscard]] int compare(QChar c, Qt::CaseSensitivity cs) const noexcept @@ -449,6 +456,15 @@ inline QStringView qToStringViewIgnoringNull(const QStringLike &s) noexcept R{{char16_t(c), u'\0'}} ; } +// QBasicUtf8StringView functions: + +template +[[nodiscard]] int QBasicUtf8StringView::compare(QStringView other, + Qt::CaseSensitivity cs) const noexcept +{ + return QtPrivate::compareStrings(*this, other, cs); +} + QT_END_NAMESPACE #endif /* QSTRINGVIEW_H */ diff --git a/src/corelib/text/qutf8stringview.h b/src/corelib/text/qutf8stringview.h index 3baac9c885e..14a8a16e62b 100644 --- a/src/corelib/text/qutf8stringview.h +++ b/src/corelib/text/qutf8stringview.h @@ -280,6 +280,17 @@ public: [[nodiscard]] constexpr qsizetype length() const noexcept { return size(); } + [[nodiscard]] int compare(QBasicUtf8StringView other, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept + { + return QtPrivate::compareStrings(*this, other, cs); + } + + [[nodiscard]] int compare(QStringView other, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + [[nodiscard]] int compare(QLatin1StringView other, + Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; + private: [[nodiscard]] static inline int compare(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept { diff --git a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp index 47c1610d2aa..bee958e57d5 100644 --- a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -406,6 +406,8 @@ private Q_SLOTS: void member_compare_QStringView_QStringView() { member_compare_impl(); } void member_compare_QStringView_QLatin1String_data() { member_compare_data(); } void member_compare_QStringView_QLatin1String() { member_compare_impl(); } + void member_compare_QStringView_QUtf8StringView_data() { member_compare_data(); } + void member_compare_QStringView_QUtf8StringView() { member_compare_impl(); } #ifdef NOT_YET_IMPLEMENTED void member_compare_QStringView_QByteArray_data() { member_compare_data(); } void member_compare_QStringView_QByteArray() { member_compare_impl(); } @@ -427,6 +429,8 @@ private Q_SLOTS: void member_compare_QLatin1String_QStringView() { member_compare_impl(); } void member_compare_QLatin1String_QLatin1String_data() { member_compare_data(); } void member_compare_QLatin1String_QLatin1String() { member_compare_impl(); } + void member_compare_QLatin1String_QUtf8StringView_data() { member_compare_data(); } + void member_compare_QLatin1String_QUtf8StringView() { member_compare_impl(); } #ifdef NOT_YET_IMPLEMENTED void member_compare_QLatin1String_QByteArray_data() { member_compare_data(); } void member_compare_QLatin1String_QByteArray() { member_compare_impl(); } @@ -470,6 +474,19 @@ private Q_SLOTS: void member_compare_QByteArrayView_const_char_star_data() { member_compare_data(); } void member_compare_QByteArrayView_const_char_star() { member_compare_impl(); } +#ifdef NOT_YET_IMPLEMENTED + void member_compare_QUtf8StringView_QChar_data() { member_compare_data(false); } + void member_compare_QUtf8StringView_QChar() { member_compare_impl(); } + void member_compare_QUtf8StringView_char16_t_data() { member_compare_data(false); } + void member_compare_QUtf8StringView_char16_t() { member_compare_impl(); } +#endif + void member_compare_QUtf8StringView_QString_data() { member_compare_data(); } + void member_compare_QUtf8StringView_QString() { member_compare_impl(); } + void member_compare_QUtf8StringView_QLatin1String_data() { member_compare_data(); } + void member_compare_QUtf8StringView_QLatin1String() { member_compare_impl(); } + void member_compare_QUtf8StringView_QUtf8StringView_data() { member_compare_data(); } + void member_compare_QUtf8StringView_QUtf8StringView() { member_compare_impl(); } + private: void localeAwareCompare_data(); template diff --git a/tests/benchmarks/corelib/text/CMakeLists.txt b/tests/benchmarks/corelib/text/CMakeLists.txt index b81bf3e07af..8f0742095d4 100644 --- a/tests/benchmarks/corelib/text/CMakeLists.txt +++ b/tests/benchmarks/corelib/text/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(qstringlist) add_subdirectory(qstringtokenizer) add_subdirectory(qregularexpression) add_subdirectory(qstring) +add_subdirectory(qutf8stringview) diff --git a/tests/benchmarks/corelib/text/qutf8stringview/CMakeLists.txt b/tests/benchmarks/corelib/text/qutf8stringview/CMakeLists.txt new file mode 100644 index 00000000000..14613afe56f --- /dev/null +++ b/tests/benchmarks/corelib/text/qutf8stringview/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_bench_qutf8stringview Binary: +##################################################################### + +qt_internal_add_benchmark(tst_bench_qutf8stringview + SOURCES + tst_bench_qutf8stringview.cpp + LIBRARIES + Qt::Test + Qt::CorePrivate +) diff --git a/tests/benchmarks/corelib/text/qutf8stringview/tst_bench_qutf8stringview.cpp b/tests/benchmarks/corelib/text/qutf8stringview/tst_bench_qutf8stringview.cpp new file mode 100644 index 00000000000..12865438286 --- /dev/null +++ b/tests/benchmarks/corelib/text/qutf8stringview/tst_bench_qutf8stringview.cpp @@ -0,0 +1,263 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause + +#include +#include +#include +#include +#include + +class tst_QUtf8StringView : public QObject +{ + Q_OBJECT + +private slots: + void equalStringsLatin1_data() { equalStrings_data(); } + void equalStringsLatin1(); + void equalStringsUtf16_data() { equalStrings_data(); } + void equalStringsUtf16(); + void equalStringsUtf8_data() { equalStrings_data(); } + void equalStringsUtf8(); + + void compareStringsCaseSensitiveLatin1_data() { compareStringsCaseSensitive_data(); } + void compareStringsCaseSensitiveLatin1() { compareStringsLatin1(true); } + void compareStringsCaseSensitiveUtf16_data() { compareStringsCaseSensitive_data(); } + void compareStringsCaseSensitiveUtf16() { compareStringsUtf16(true); } + void compareStringsCaseSensitiveUtf8_data() { compareStringsCaseSensitive_data(); } + void compareStringsCaseSensitiveUtf8() { compareStringsUtf8(true); } + + void compareStringsCaseInsensitiveLatin1_data() { compareStringsCaseInsensitive_data(); } + void compareStringsCaseInsensitiveLatin1() { compareStringsLatin1(false); } + void compareStringsCaseInsensitiveUtf16_data() { compareStringsCaseInsensitive_data(); } + void compareStringsCaseInsensitiveUtf16() { compareStringsUtf16(false); } + void compareStringsCaseInsensitiveUtf8_data() { compareStringsCaseInsensitive_data(); } + void compareStringsCaseInsensitiveUtf8() { compareStringsUtf8(false); } + + void compareStringsWithErrors_data(); + void compareStringsWithErrors(); + +private: + void equalStrings_data(); + void compareStringsCaseSensitive_data(); + void compareStringsCaseInsensitive_data(); + void compareStringsLatin1(bool caseSensitive); + void compareStringsUtf16(bool caseSensitive); + void compareStringsUtf8(bool caseSensitive); +}; + +void tst_QUtf8StringView::equalStrings_data() +{ + QTest::addColumn("lhs"); + QTest::addColumn("rhs"); + QTest::addColumn("isEqual"); + + QTest::newRow("EqualStrings") << "Test" + << "Test" << true; + QTest::newRow("EqualStringsLong") + << "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + << "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" << true; + QTest::newRow("DifferentCase") << "test" + << "Test" << false; + QTest::newRow("DifferentCaseLong") + << "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + << "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" << false; + QTest::newRow("ReverseStrings") << "Test" + << "tseT" << false; + QTest::newRow("Latin1RangeCharacter") << u8"B\u00d8" << u8"B\u00d8" << true; + QTest::newRow("Latin1RangeCharacterDifferentCase") << u8"B\u00d8" << u8"B\u00f8" << false; +} + +void tst_QUtf8StringView::equalStringsLatin1() +{ + QFETCH(QString, lhs); + QFETCH(QString, rhs); + QFETCH(bool, isEqual); + QByteArray left = lhs.toUtf8(); + QByteArray right = rhs.toLatin1(); + QBasicUtf8StringView lhv(left); + QLatin1StringView rhv(right); + bool result; + + QBENCHMARK { + result = QtPrivate::equalStrings(lhv, rhv); + }; + QCOMPARE(result, isEqual); +} + +void tst_QUtf8StringView::equalStringsUtf16() +{ + QFETCH(QString, lhs); + QFETCH(QString, rhs); + QFETCH(bool, isEqual); + + QByteArray left = lhs.toUtf8(); + QBasicUtf8StringView lhv(left); + QStringView rhv(rhs); + bool result; + + QBENCHMARK { + result = QtPrivate::equalStrings(lhv, rhv); + }; + QCOMPARE(result, isEqual); +} + +void tst_QUtf8StringView::equalStringsUtf8() +{ + QFETCH(QString, lhs); + QFETCH(QString, rhs); + QFETCH(bool, isEqual); + + QByteArray left = lhs.toUtf8(); + QByteArray right = rhs.toUtf8(); + QBasicUtf8StringView lhv(left); + QBasicUtf8StringView rhv(right); + bool result; + + QBENCHMARK { + result = QtPrivate::equalStrings(lhv, rhv); + }; + QCOMPARE(result, isEqual); +} + +void tst_QUtf8StringView::compareStringsCaseSensitive_data() +{ + QTest::addColumn("lhs"); + QTest::addColumn("rhs"); + QTest::addColumn("compareValue"); + + QTest::newRow("EqualStrings") << "Test" + << "Test" << 0; + QTest::newRow("EqualStringsLong") << "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ" + << "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ" << 0; + QTest::newRow("DifferentCase") << "test" + << "Test" << 32; + QTest::newRow("DifferentCaseLong") + << "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ" + << "abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz" << -32; + QTest::newRow("ReverseStrings") << "Test" + << "tseT" << -32; + QTest::newRow("Different Strings") << "Testing and testing" + << "Testing and resting" << 2; + QTest::newRow("Latin1RangeCharacter") << u8"B\u00d8" << u8"B\u00d8" << 0; + QTest::newRow("Latin1RangeCharacterDifferentCase") << u8"B\u00d8" << u8"B\u00f8" << -32; +} + +void tst_QUtf8StringView::compareStringsCaseInsensitive_data() +{ + QTest::addColumn("lhs"); + QTest::addColumn("rhs"); + QTest::addColumn("compareValue"); + + QTest::newRow("EqualStrings") << "Test" + << "Test" << 0; + QTest::newRow("EqualStringsLong") << "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ" + << "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ" << 0; + QTest::newRow("DifferentCase") << "test" + << "Test" << 0; + QTest::newRow("DifferentCaseLong") << "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ" + << "abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz" << 0; + QTest::newRow("ReverseStrings") << "Test" + << "tseT" << -14; + QTest::newRow("Different Strings") << "Testing and testing" + << "Testing and resting" << 2; + QTest::newRow("Latin1RangeCharacter") << u8"B\u00d8" << u8"B\u00d8" << 0; + QTest::newRow("Latin1RangeCharacterDifferentCase") << u8"B\u00d8" << u8"B\u00f8" << 0; +} + +void tst_QUtf8StringView::compareStringsLatin1(bool caseSensitive) +{ + QFETCH(QString, lhs); + QFETCH(QString, rhs); + QFETCH(int, compareValue); + QByteArray left = lhs.toUtf8(); + QByteArray right = rhs.toLatin1(); + QBasicUtf8StringView lhv(left); + QLatin1StringView rhv(right); + Qt::CaseSensitivity cs = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + int result; + + QBENCHMARK { + result = lhv.compare(rhv, cs); + }; + QCOMPARE(result, compareValue); +} + +void tst_QUtf8StringView::compareStringsUtf16(bool caseSensitive) +{ + QFETCH(QString, lhs); + QFETCH(QString, rhs); + QFETCH(int, compareValue); + + QByteArray left = lhs.toUtf8(); + QBasicUtf8StringView lhv(left); + QStringView rhv(rhs); + Qt::CaseSensitivity cs = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + int result; + + QBENCHMARK { + result = lhv.compare(rhv, cs); + }; + QCOMPARE(result, compareValue); +} + +void tst_QUtf8StringView::compareStringsUtf8(bool caseSensitive) +{ + QFETCH(QString, lhs); + QFETCH(QString, rhs); + QFETCH(int, compareValue); + + QByteArray left = lhs.toUtf8(); + QByteArray right = rhs.toUtf8(); + QBasicUtf8StringView lhv(left); + QBasicUtf8StringView rhv(right); + Qt::CaseSensitivity cs = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + int result; + + QBENCHMARK { + result = lhv.compare(rhv, cs); + }; + QCOMPARE(result, compareValue); +} + +void tst_QUtf8StringView::compareStringsWithErrors_data() +{ + QTest::addColumn("lhs"); + QTest::addColumn("rhs"); + QTest::addColumn("compare"); + QTest::addColumn("caseSensitive"); + + QTest::newRow("Compare errors 0xfe vs 0xff case-insensitive") + << QByteArray("\xfe") << QByteArray("\xff") << 0 << false; + QTest::newRow("Compare errors 0xff vs 0xff case-insensitive") + << QByteArray("\xff") << QByteArray("\xff") << 0 << false; + QTest::newRow("Compare 'a' with error 0xff case-insensitive") + << QByteArray("a") << QByteArray("\xff") << -65436 << false; + QTest::newRow("Compare errors 0xfe vs 0xff case-sensitive") + << QByteArray("\xfe") << QByteArray("\xff") << -1 << true; + QTest::newRow("Compare errors 0xff vs 0xff case-sensitive") + << QByteArray("\xff") << QByteArray("\xff") << 0 << true; + QTest::newRow("Compare 'a' with error 0xff case-sensitive") + << QByteArray("a") << QByteArray("\xff") << -158 << true; +} + +void tst_QUtf8StringView::compareStringsWithErrors() +{ + QFETCH(QByteArray, lhs); + QFETCH(QByteArray, rhs); + QFETCH(int, compare); + QFETCH(bool, caseSensitive); + QBasicUtf8StringView lhv(lhs); + QBasicUtf8StringView rhv(rhs); + Qt::CaseSensitivity cs = caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive; + int result; + + QBENCHMARK { + result = lhv.compare(rhv, cs); + }; + QCOMPARE(result, compare); + QCOMPARE(-result, rhv.compare(lhv, cs)); +} + +QTEST_MAIN(tst_QUtf8StringView) + +#include "tst_bench_qutf8stringview.moc"