diff options
Diffstat (limited to 'src/corelib/text/qanystringview.h')
-rw-r--r-- | src/corelib/text/qanystringview.h | 241 |
1 files changed, 149 insertions, 92 deletions
diff --git a/src/corelib/text/qanystringview.h b/src/corelib/text/qanystringview.h index 0cd97fd54e..075d64679c 100644 --- a/src/corelib/text/qanystringview.h +++ b/src/corelib/text/qanystringview.h @@ -1,22 +1,33 @@ +// Copyright (C) 2022 The Qt Company Ltd. // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com> // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QANYSTRINGVIEW_H #define QANYSTRINGVIEW_H +#include <QtCore/qcompare.h> +#include <QtCore/qlatin1stringview.h> #include <QtCore/qstringview.h> #include <QtCore/qutf8stringview.h> #ifdef __cpp_impl_three_way_comparison #include <compare> #endif +#include <QtCore/q20type_traits.h> #include <limits> class tst_QAnyStringView; QT_BEGIN_NAMESPACE -template <typename, typename> class QStringBuilder; -template <typename> struct QConcatenable; +namespace QtPrivate { + +template <typename Tag, typename Result> +struct wrapped { using type = Result; }; + +template <typename Tag, typename Result> +using wrapped_t = typename wrapped<Tag, Result>::type; + +} // namespace QtPrivate class QAnyStringView { @@ -24,11 +35,40 @@ public: typedef qptrdiff difference_type; typedef qsizetype size_type; private: + static constexpr size_t SizeMask = (std::numeric_limits<size_t>::max)() / 4; +#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) + static constexpr int SizeShift = 2; + static constexpr size_t Latin1Flag = 1; +#else + static constexpr int SizeShift = 0; + static constexpr size_t Latin1Flag = SizeMask + 1; +#endif + static constexpr size_t TwoByteCodePointFlag = Latin1Flag << 1; + static constexpr size_t TypeMask = ~(SizeMask << SizeShift); + static_assert(TypeMask == (Latin1Flag|TwoByteCodePointFlag)); + + // Tag bits + // 0 0 Utf8 + // 0 1 Latin1 + // 1 0 Utf16 + // 1 1 Unused + // ^ ^ latin1 + // | sizeof code-point == 2 + enum Tag : size_t { + Utf8 = 0, + Latin1 = Latin1Flag, + Utf16 = TwoByteCodePointFlag, + Unused = TypeMask, + }; + template <typename Char> - using if_compatible_char = std::enable_if_t<std::disjunction_v< + using is_compatible_char = std::disjunction< QtPrivate::IsCompatibleCharType<Char>, QtPrivate::IsCompatibleChar8Type<Char> - >, bool>; + >; + + template <typename Char> + using if_compatible_char = std::enable_if_t<is_compatible_char<Char>::value, bool>; template <typename Pointer> using if_compatible_pointer = std::enable_if_t<std::disjunction_v< @@ -43,6 +83,21 @@ private: QtPrivate::IsContainerCompatibleWithQUtf8StringView<T> >, bool>; + template <typename QStringOrQByteArray, typename T> + using if_convertible_to = std::enable_if_t<std::conjunction_v< + // need to exclude a bunch of stuff, because we take by universal reference: + std::negation<std::disjunction< + std::is_same<q20::remove_cvref_t<T>, QAnyStringView::Tag>, + std::is_same<q20::remove_cvref_t<T>, QAnyStringView>, // don't make a copy/move ctor + std::is_pointer<std::decay_t<T>>, // const char*, etc + is_compatible_char<T>, // don't create a QString/QByteArray, we have a ctor + std::is_same<q20::remove_cvref_t<T>, QByteArray>, + std::is_same<q20::remove_cvref_t<T>, QString> + >>, + // this is what we're really after: + std::is_convertible<T, QStringOrQByteArray> + >, bool>; + // confirm we don't make an accidental copy constructor: static_assert(QtPrivate::IsContainerCompatibleWithQStringView<QAnyStringView>::value == false); static_assert(QtPrivate::IsContainerCompatibleWithQUtf8StringView<QAnyStringView>::value == false); @@ -51,15 +106,8 @@ private: static constexpr bool isAsciiOnlyCharsAtCompileTime(Char *str, qsizetype sz) noexcept { // do not perform check if not at compile time -#if defined(__cpp_lib_is_constant_evaluated) - if (!std::is_constant_evaluated()) + if (!q20::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); @@ -69,8 +117,8 @@ private: if (uchar(str[i]) > 0x7f) return false; } + return true; } - return true; } template<typename Char> @@ -80,45 +128,31 @@ private: Q_ASSERT(sz >= 0); Q_ASSERT(sz <= qsizetype(SizeMask)); Q_ASSERT(str || !sz); - return std::size_t(sz) | uint(sizeof(Char) == sizeof(char16_t)) * Tag::Utf16 + return (std::size_t(sz) << SizeShift) + | uint(sizeof(Char) == sizeof(char16_t)) * Tag::Utf16 | uint(isAsciiOnlyCharsAtCompileTime(str, sz)) * Tag::Latin1; } template <typename Char> - static qsizetype lengthHelperPointer(const Char *str) noexcept + static constexpr qsizetype lengthHelperPointer(const Char *str) noexcept { -#if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) - if (__builtin_constant_p(*str)) { - qsizetype result = 0; - while (*str++ != u'\0') - ++result; - return result; - } -#endif + if (q20::is_constant_evaluated()) + return QtPrivate::lengthHelperPointer(str); if constexpr (sizeof(Char) == sizeof(char16_t)) return QtPrivate::qustrlen(reinterpret_cast<const char16_t*>(str)); else return qsizetype(strlen(reinterpret_cast<const char*>(str))); } - template <typename Container> - static constexpr qsizetype lengthHelperContainer(const Container &c) noexcept - { - return qsizetype(std::size(c)); - } - - template <typename Char, size_t N> - static constexpr qsizetype lengthHelperContainer(const Char (&str)[N]) noexcept - { - const auto it = std::char_traits<Char>::find(str, N, Char(0)); - const auto end = it ? it : std::next(str, N); - return qsizetype(std::distance(str, end)); - } - static QChar toQChar(char ch) noexcept { return toQChar(QLatin1Char{ch}); } // we don't handle UTF-8 multibytes static QChar toQChar(QChar ch) noexcept { return ch; } static QChar toQChar(QLatin1Char ch) noexcept { return ch; } + struct QCharContainer { // private, so users can't pass their own + explicit QCharContainer() = default; + QChar ch; + }; + explicit constexpr QAnyStringView(const void *d, qsizetype n, std::size_t sizeAndType) noexcept : m_data{d}, m_size{std::size_t(n) | (sizeAndType & TypeMask)} {} public: @@ -137,7 +171,7 @@ public: constexpr QAnyStringView(const Char *f, const Char *l) : QAnyStringView(f, l - f) {} -#ifdef Q_CLANG_QDOC +#ifdef Q_QDOC template <typename Char, size_t N> constexpr QAnyStringView(const Char (&array)[N]) noexcept; @@ -155,32 +189,37 @@ public: inline QAnyStringView(const QString &str) noexcept; inline constexpr QAnyStringView(QLatin1StringView str) noexcept; - // defined in qstringbuilder.h - template <typename A, typename B> - inline QAnyStringView(const QStringBuilder<A, B> &expr, - typename QConcatenable<QStringBuilder<A, B>>::ConvertTo &&capacity = {}); - template <typename Container, if_compatible_container<Container> = true> - constexpr QAnyStringView(const Container &c) noexcept - : QAnyStringView(std::data(c), lengthHelperContainer(c)) {} - + constexpr Q_ALWAYS_INLINE QAnyStringView(const Container &c) noexcept + : QAnyStringView(std::data(c), QtPrivate::lengthHelperContainer(c)) {} + + template <typename Container, if_convertible_to<QString, Container> = true> + constexpr QAnyStringView(Container &&c, QtPrivate::wrapped_t<Container, QString> &&capacity = {}) + //noexcept(std::is_nothrow_constructible_v<QString, Container>) + : QAnyStringView(capacity = std::forward<Container>(c)) {} + + template <typename Container, if_convertible_to<QByteArray, Container> = true> + constexpr QAnyStringView(Container &&c, QtPrivate::wrapped_t<Container, QByteArray> &&capacity = {}) + //noexcept(std::is_nothrow_constructible_v<QByteArray, Container>) + : QAnyStringView(capacity = std::forward<Container>(c)) {} template <typename Char, if_compatible_char<Char> = true> constexpr QAnyStringView(const Char &c) noexcept : QAnyStringView{&c, 1} {} - constexpr QAnyStringView(const QChar &c) noexcept - : QAnyStringView{&c, 1} {} + template <typename Char, if_convertible_to<QChar, Char> = true> + constexpr QAnyStringView(Char ch, QCharContainer &&capacity = QCharContainer()) noexcept + : QAnyStringView{&(capacity.ch = ch), 1} {} template <typename Char, typename Container = decltype(QChar::fromUcs4(U'x')), std::enable_if_t<std::is_same_v<Char, char32_t>, bool> = true> - constexpr QAnyStringView(Char c, Container &&capacity = {}) + constexpr QAnyStringView(Char c, Container &&capacity = {}) noexcept : QAnyStringView(capacity = QChar::fromUcs4(c)) {} constexpr QAnyStringView(QStringView v) noexcept - : QAnyStringView(std::data(v), lengthHelperContainer(v)) {} + : QAnyStringView(std::data(v), QtPrivate::lengthHelperContainer(v)) {} template <bool UseChar8T> constexpr QAnyStringView(QBasicUtf8StringView<UseChar8T> v) noexcept - : QAnyStringView(std::data(v), lengthHelperContainer(v)) {} + : QAnyStringView(std::data(v), QtPrivate::lengthHelperContainer(v)) {} template <typename Char, size_t Size, if_compatible_char<Char> = true> [[nodiscard]] constexpr static QAnyStringView fromArray(const Char (&string)[Size]) noexcept @@ -190,21 +229,62 @@ public: template <typename Visitor> inline constexpr decltype(auto) visit(Visitor &&v) const; + [[nodiscard]] + constexpr QAnyStringView mid(qsizetype pos, qsizetype n = -1) const + { + using namespace QtPrivate; + auto result = QContainerImplHelper::mid(size(), &pos, &n); + return result == QContainerImplHelper::Null ? QAnyStringView() : sliced(pos, n); + } + [[nodiscard]] + constexpr QAnyStringView left(qsizetype n) const + { + if (size_t(n) >= size_t(size())) + n = size(); + return sliced(0, n); + } + [[nodiscard]] + constexpr QAnyStringView right(qsizetype n) const + { + if (size_t(n) >= size_t(size())) + n = size(); + return sliced(size() - n, n); + } + + [[nodiscard]] constexpr QAnyStringView sliced(qsizetype pos) const + { verify(pos, 0); auto r = *this; r.advanceData(pos); r.setSize(size() - pos); return r; } + [[nodiscard]] constexpr QAnyStringView sliced(qsizetype pos, qsizetype n) const + { verify(pos, n); auto r = *this; r.advanceData(pos); r.setSize(n); return r; } + [[nodiscard]] constexpr QAnyStringView first(qsizetype n) const + { verify(0, n); return sliced(0, n); } + [[nodiscard]] constexpr QAnyStringView last(qsizetype n) const + { verify(0, n); return sliced(size() - n, n); } + [[nodiscard]] constexpr QAnyStringView chopped(qsizetype n) const + { verify(0, n); return sliced(0, size() - n); } + + constexpr void truncate(qsizetype n) + { verify(0, n); setSize(n); } + constexpr void chop(qsizetype n) + { verify(0, n); setSize(size() - n); } + + [[nodiscard]] inline QString toString() const; // defined in qstring.h - [[nodiscard]] constexpr qsizetype size() const noexcept { return qsizetype(m_size & SizeMask); } + [[nodiscard]] constexpr qsizetype size() const noexcept + { return qsizetype((m_size >> SizeShift) & SizeMask); } [[nodiscard]] constexpr const void *data() const noexcept { return m_data; } [[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 +#ifdef QT_SUPPORTS_IS_CONSTANT_EVALUATED true #else false #endif ; + // // STL compatibility API: // @@ -223,47 +303,20 @@ public: { return size(); } private: - [[nodiscard]] friend inline bool operator==(QAnyStringView lhs, QAnyStringView rhs) noexcept + friend bool comparesEqual(const QAnyStringView &lhs, const QAnyStringView &rhs) noexcept { return QAnyStringView::equal(lhs, rhs); } - [[nodiscard]] friend inline bool operator!=(QAnyStringView lhs, QAnyStringView rhs) noexcept - { return !QAnyStringView::equal(lhs, rhs); } + friend Qt::strong_ordering + compareThreeWay(const QAnyStringView &lhs, const QAnyStringView &rhs) noexcept + { + const int res = QAnyStringView::compare(lhs, rhs); + return Qt::compareThreeWay(res, 0); + } + Q_DECLARE_STRONGLY_ORDERED(QAnyStringView) -#if defined(__cpp_impl_three_way_comparison) && !defined(Q_QDOC) - [[nodiscard]] friend inline auto operator<=>(QAnyStringView lhs, QAnyStringView rhs) noexcept - { return QAnyStringView::compare(lhs, rhs) <=> 0; } -#else - [[nodiscard]] friend inline bool operator<=(QAnyStringView lhs, QAnyStringView rhs) noexcept - { return QAnyStringView::compare(lhs, rhs) <= 0; } - [[nodiscard]] friend inline bool operator>=(QAnyStringView lhs, QAnyStringView rhs) noexcept - { return QAnyStringView::compare(lhs, rhs) >= 0; } - [[nodiscard]] friend inline bool operator<(QAnyStringView lhs, QAnyStringView rhs) noexcept - { return QAnyStringView::compare(lhs, rhs) < 0; } - [[nodiscard]] friend inline bool operator>(QAnyStringView lhs, QAnyStringView rhs) noexcept - { return QAnyStringView::compare(lhs, rhs) > 0; } +#ifndef QT_NO_DEBUG_STREAM + Q_CORE_EXPORT friend QDebug operator<<(QDebug d, QAnyStringView s); #endif - // TODO: Optimize by inverting and storing the flags in the low bits and - // the size in the high. - static_assert(std::is_same_v<std::size_t, size_t>); - static_assert(sizeof(size_t) == sizeof(qsizetype)); - static constexpr size_t SizeMask = (std::numeric_limits<size_t>::max)() / 4; - static constexpr size_t Latin1Flag = SizeMask + 1; - static constexpr size_t TwoByteCodePointFlag = Latin1Flag << 1; - static constexpr size_t TypeMask = (std::numeric_limits<size_t>::max)() & ~SizeMask; - static_assert(TypeMask == (Latin1Flag|TwoByteCodePointFlag)); - // HI HI LO LO ... - // 0 0 SZ SZ Utf8 - // 0 1 SZ SZ Latin1 - // 1 0 SZ SZ Utf16 - // 1 1 SZ SZ Unused - // ^ ^ latin1 - // | sizeof code-point == 2 - enum Tag : size_t { - Utf8 = 0, - Latin1 = Latin1Flag, - Utf16 = TwoByteCodePointFlag, - Unused = TypeMask, - }; [[nodiscard]] constexpr Tag tag() const noexcept { return Tag{m_size & TypeMask}; } [[nodiscard]] constexpr bool isUtf16() const noexcept { return tag() == Tag::Utf16; } [[nodiscard]] constexpr bool isUtf8() const noexcept { return tag() == Tag::Utf8; } @@ -274,7 +327,11 @@ private: { return Q_ASSERT(isUtf8()), q_no_char8_t::QUtf8StringView{m_data_utf8, size()}; } [[nodiscard]] inline constexpr QLatin1StringView asLatin1StringView() const; [[nodiscard]] constexpr size_t charSize() const noexcept { return isUtf16() ? 2 : 1; } - Q_ALWAYS_INLINE constexpr void verify(qsizetype pos, qsizetype n = 0) const + constexpr void setSize(qsizetype sz) noexcept { m_size = size_t(sz) | tag(); } + constexpr void advanceData(qsizetype delta) noexcept + { m_data_utf8 += delta * charSize(); } + Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0, + [[maybe_unused]] qsizetype n = 1) const { Q_ASSERT(pos >= 0); Q_ASSERT(pos <= size()); @@ -296,7 +353,7 @@ template <typename QStringLike, std::enable_if_t<std::disjunction_v< std::is_same<QStringLike, QByteArray> >, bool> = true> [[nodiscard]] inline QAnyStringView qToAnyStringViewIgnoringNull(const QStringLike &s) noexcept -{ return QAnyStringView(s.data(), s.size()); } +{ return QAnyStringView(s.begin(), s.size()); } QT_END_NAMESPACE |