QAnyStringView: constexpr detect 8-bit ASCII as Latin 1

This has performance benefits when doing comparisons.
The check is only performed at compile time.

Task-number: QTBUG-101014
Change-Id: I55694b045fe5e75d9671d0a3a70c80d998cf98c8
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Øystein Heskestad 2022-03-09 17:24:53 +01:00
parent 64847af6e6
commit eaabd0c545
2 changed files with 67 additions and 7 deletions

View File

@ -47,6 +47,8 @@
#endif
#include <limits>
class tst_QAnyStringView;
QT_BEGIN_NAMESPACE
template <typename, typename> class QStringBuilder;
@ -81,14 +83,41 @@ private:
static_assert(QtPrivate::IsContainerCompatibleWithQStringView<QAnyStringView>::value == false);
static_assert(QtPrivate::IsContainerCompatibleWithQUtf8StringView<QAnyStringView>::value == false);
template <typename Char>
static constexpr std::size_t encodeType(qsizetype sz) noexcept
template<typename Char>
static constexpr bool isAsciiOnlyCharsAtCompileTime(Char *str, qsizetype sz) noexcept
{
// only deals with Utf8 and Utf16 - there's only one way to create
// a Latin1 string, and that ctor deals with the tag itself
// do not perform check if not at compile time
#if defined(__cpp_lib_is_constant_evaluated)
if (!std::is_constant_evaluated())
return false;
#elif defined(Q_CC_GNU) && !defined(Q_CC_CLANG)
if (!str || !__builtin_constant_p(*str))
return false;
#else
return false;
#endif
if constexpr (sizeof(Char) != sizeof(char)) {
Q_UNUSED(str);
Q_UNUSED(sz);
return false;
} else {
for (qsizetype i = 0; i < sz; ++i) {
if (uchar(str[i]) > 0x7f)
return false;
}
}
return true;
}
template<typename Char>
static constexpr std::size_t encodeType(const Char *str, qsizetype sz) noexcept
{
// Utf16 if 16 bit, Latin1 if ASCII, else Utf8
Q_ASSERT(sz >= 0);
Q_ASSERT(sz <= qsizetype(SizeMask));
return std::size_t(sz) | uint(sizeof(Char) == sizeof(char16_t)) * Tag::Utf16;
Q_ASSERT(str || !sz);
return std::size_t(sz) | uint(sizeof(Char) == sizeof(char16_t)) * Tag::Utf16
| uint(isAsciiOnlyCharsAtCompileTime(str, sz)) * Tag::Latin1;
}
template <typename Char>
@ -136,8 +165,9 @@ public:
template <typename Char, if_compatible_char<Char> = true>
constexpr QAnyStringView(const Char *str, qsizetype len)
: m_data{str},
m_size{encodeType<Char>((Q_ASSERT(len >= 0), Q_ASSERT(str || !len), len))} {}
: m_data{str}, m_size{encodeType<Char>(str, len)}
{
}
template <typename Char, if_compatible_char<Char> = true>
constexpr QAnyStringView(const Char *f, const Char *l)
@ -204,6 +234,13 @@ public:
[[nodiscard]] Q_CORE_EXPORT static int compare(QAnyStringView lhs, QAnyStringView rhs, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
[[nodiscard]] Q_CORE_EXPORT static bool equal(QAnyStringView lhs, QAnyStringView rhs) noexcept;
static constexpr inline bool detects_US_ASCII_at_compile_time =
#ifdef __cpp_lib_is_constant_evaluated
true
#else
false
#endif
;
//
// STL compatibility API:
//
@ -286,6 +323,7 @@ private:
const char16_t *m_data_utf16;
};
size_t m_size;
friend class ::tst_QAnyStringView;
};
Q_DECLARE_TYPEINFO(QAnyStringView, Q_PRIMITIVE_TYPE);

View File

@ -246,6 +246,7 @@ class tst_QAnyStringView : public QObject
private Q_SLOTS:
void constExpr() const;
void basics() const;
void asciiLiteralIsLatin1() const;
void fromQString() const { fromQStringOrByteArray<QString>(); }
void fromQByteArray() const { fromQStringOrByteArray<QByteArray>(); }
@ -431,6 +432,27 @@ void tst_QAnyStringView::basics() const
QVERIFY(!(sv2 != sv1));
}
void tst_QAnyStringView::asciiLiteralIsLatin1() const
{
if constexpr (QAnyStringView::detects_US_ASCII_at_compile_time) {
constexpr bool asciiCstringIsLatin1 = QAnyStringView("Hello, World").isLatin1();
QVERIFY(asciiCstringIsLatin1);
constexpr bool asciiUtf8stringIsLatin1 = QAnyStringView(u8"Hello, World").isLatin1();
QVERIFY(asciiUtf8stringIsLatin1);
constexpr bool utf8StringIsNotLatin1 = !QAnyStringView(u8"Tørrfisk").isLatin1();
QVERIFY(utf8StringIsNotLatin1);
constexpr bool asciiCstringArrayIsLatin1 =
QAnyStringView::fromArray("Hello, World").isLatin1();
QVERIFY(asciiCstringArrayIsLatin1);
constexpr bool asciiUtfstringArrayIsLatin1 =
QAnyStringView::fromArray(u8"Hello, World").isLatin1();
QVERIFY(asciiUtfstringArrayIsLatin1);
constexpr bool utf8StringArrayIsNotLatin1 =
!QAnyStringView::fromArray(u8"Tørrfisk").isLatin1();
QVERIFY(utf8StringArrayIsNotLatin1);
}
}
template <typename StringBuilder>
void tst_QAnyStringView::fromQStringBuilder(StringBuilder &&sb, QStringView expected) const
{