summaryrefslogtreecommitdiffstats
path: root/src/corelib/text/qanystringview.h
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@kdab.com>2020-05-20 15:20:07 +0200
committerLars Knoll <lars.knoll@qt.io>2020-08-31 22:57:49 +0200
commit2c9529e158fc589c48e6b1fb61dca2133e33ac4d (patch)
treee9429715ab339d95c2ba9456bf39a894e8b41539 /src/corelib/text/qanystringview.h
parentf0ae973244026ca5382f05630bd799b44154d224 (diff)
Long live Q{Any,Utf8}StringView!
We need to add these two classes at the same time, because QAnyStringView makes all QUtf8StringView relational operators moot. We might want to add some later, esp. for UTF-8/UTf-8 comparisons, to avoid the pessimization that we can't early-out on size() mismatch in QAnyStringView equality operators, but that's an optimization, not a correctness issue, and can be fixed in a source-compatible way even after Qt 6 is released. To deal with the char8_t problem in C++20, make QUtf8StringView a class template out of which two UTF-8 views can be instantiated: the Qt 7 version, which depends on C++20 char8_t as value_type, and the Qt 6 version where value_type is a char. Use inline namespaces to map the QUtf8StringView identifier to one or the other, depending on the C++ version used to compile the user code. The inline namespace names must needs be a bit ugly, as their inline'ness depends on __cpp_char8_t. If we simply used q_v1/q_v2 we'd be blocking these names for Qt inline namespaces forever, because it's likely that inline'ness of other users of inline namespaces in Qt depends on things other than __cpp_char8_t. While inline'ness of namespaces is, theoretically speaking, a compile-time-only property, at least Clang warns about mixed use of inline on a given namespace, so we need to bite the bullet here. This is also the reason for the QT_BEGIN_..._NAMESPACE macros: GCC is ok with the first declaration making a namespace inline, while Clang warns upon re-opening an inline namespace as a non-inline one. [ChangeLog][QtCore][QUtf8StringView] New class. [ChangeLog][QtCore][QAnyStringView] New class. Change-Id: Ia7179760fca0e0b67d52f5accb0a62e389b17913 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/text/qanystringview.h')
-rw-r--r--src/corelib/text/qanystringview.h292
1 files changed, 292 insertions, 0 deletions
diff --git a/src/corelib/text/qanystringview.h b/src/corelib/text/qanystringview.h
new file mode 100644
index 0000000000..61761c6a82
--- /dev/null
+++ b/src/corelib/text/qanystringview.h
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QANYSTRINGVIEW_H
+#define QANYSTRINGVIEW_H
+
+#include <QtCore/qstringview.h>
+#include <QtCore/qutf8stringview.h>
+
+QT_BEGIN_NAMESPACE
+
+template <typename, typename> class QStringBuilder;
+
+class QAnyStringView
+{
+public:
+ typedef qptrdiff difference_type;
+ typedef qsizetype size_type;
+private:
+ template <typename Char>
+ using if_compatible_char = std::enable_if_t<std::disjunction_v<
+ QtPrivate::IsCompatibleCharType<Char>,
+ QtPrivate::IsCompatibleChar8Type<Char>
+ >, bool>;
+
+ template <typename Pointer>
+ using if_compatible_pointer = std::enable_if_t<std::disjunction_v<
+ QtPrivate::IsCompatiblePointer<Pointer>,
+ QtPrivate::IsCompatiblePointer8<Pointer>
+ >, bool>;
+
+
+ template <typename T>
+ using if_compatible_container = std::enable_if_t<std::disjunction_v<
+ QtPrivate::IsContainerCompatibleWithQStringView<T>,
+ QtPrivate::IsContainerCompatibleWithQUtf8StringView<T>
+ >, bool>;
+
+ // confirm we don't make an accidental copy constructor:
+ 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
+ {
+ // only deals with Utf8 and Utf16 - there's only one way to create
+ // a Latin1 string, and that ctor deals with the tag itself
+ Q_ASSERT(sz >= 0);
+ Q_ASSERT(sz <= qsizetype(SizeMask));
+ return std::size_t(sz) | uint(sizeof(Char) == sizeof(char16_t)) * Tag::Utf16;
+ }
+
+ template <typename Char>
+ static qsizetype lengthHelperPointer(const Char *str) noexcept
+ {
+#if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) && !defined(Q_CC_INTEL)
+ if (__builtin_constant_p(*str)) {
+ qsizetype result = 0;
+ while (*str++ != u'\0')
+ ++result;
+ return result;
+ }
+#endif
+ 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 (&)[N]) noexcept
+ {
+ return qsizetype(N - 1);
+ }
+
+ 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; }
+
+ 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:
+ constexpr QAnyStringView() noexcept
+ : m_data{nullptr}, m_size{0} {}
+ constexpr QAnyStringView(std::nullptr_t) noexcept
+ : QAnyStringView() {}
+
+ 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))} {}
+
+ template <typename Char, if_compatible_char<Char> = true>
+ constexpr QAnyStringView(const Char *f, const Char *l)
+ : QAnyStringView(f, l - f) {}
+
+#ifdef Q_CLANG_QDOC
+ template <typename Char, size_t N>
+ constexpr QAnyStringView(const Char (&array)[N]) noexcept;
+
+ template <typename Char>
+ constexpr QAnyStringView(const Char *str) noexcept;
+#else
+
+ template <typename Pointer, if_compatible_pointer<Pointer> = true>
+ constexpr QAnyStringView(const Pointer &str) noexcept
+ : QAnyStringView{str, str ? lengthHelperPointer(str) : 0} {}
+#endif
+
+ // defined in qstring.h
+ inline QAnyStringView(const QByteArray &str) noexcept; // TODO: Should we have this at all? Remove?
+ inline QAnyStringView(const QString &str) noexcept;
+ inline constexpr QAnyStringView(QLatin1String str) noexcept;
+
+ // defined in qstringbuilder.h
+ template <typename A, typename B>
+ inline QAnyStringView(const QStringBuilder<A, B> &expr,
+ typename 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)) {}
+
+ 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, 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 = {})
+ : QAnyStringView(capacity = QChar::fromUcs4(c)) {}
+
+ constexpr QAnyStringView(QStringView v) noexcept
+ : QAnyStringView(std::data(v), lengthHelperContainer(v)) {}
+
+ template <bool UseChar8T>
+ constexpr QAnyStringView(QBasicUtf8StringView<UseChar8T> v) noexcept
+ : QAnyStringView(std::data(v), lengthHelperContainer(v)) {}
+
+ // defined in qstring.h:
+ template <typename Visitor>
+ inline constexpr decltype(auto) visit(Visitor &&v) const;
+
+ [[nodiscard]] inline QString toString() const; // defined in qstring.h
+
+ [[nodiscard]] constexpr qsizetype size() const noexcept { return qsizetype(m_size & 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;
+
+ //
+ // STL compatibility API:
+ //
+ [[nodiscard]] constexpr QChar front() const; // NOT noexcept!
+ [[nodiscard]] constexpr QChar back() const; // NOT noexcept!
+ [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
+ [[nodiscard]] constexpr qsizetype size_bytes() const noexcept
+ { return size() * charSize(); }
+
+ //
+ // Qt compatibility API:
+ //
+ [[nodiscard]] constexpr bool isNull() const noexcept { return !m_data; }
+ [[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); }
+#if QT_DEPRECATED_SINCE(6, 0)
+ [[nodiscard]]
+ Q_DECL_DEPRECATED_X("Use size() and port callers to qsizetype.")
+ constexpr int length() const /* not nothrow! */
+ { return Q_ASSERT(int(size()) == size()), int(size()); }
+#endif
+private:
+ // 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; }
+ [[nodiscard]] constexpr bool isLatin1() const noexcept { return tag() == Tag::Latin1; }
+ [[nodiscard]] constexpr QStringView asStringView() const
+ { return Q_ASSERT(isUtf16()), QStringView{m_data_utf16, size()}; }
+ [[nodiscard]] constexpr q_no_char8_t::QUtf8StringView asUtf8StringView() const
+ { return Q_ASSERT(isUtf8()), q_no_char8_t::QUtf8StringView{m_data_utf8, size()}; }
+ [[nodiscard]] inline constexpr QLatin1String 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
+ {
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(pos <= size());
+ Q_ASSERT(n >= 0);
+ Q_ASSERT(n <= size() - pos);
+ }
+ union {
+ const void *m_data;
+ const char *m_data_utf8;
+ const char16_t *m_data_utf16;
+ };
+ size_t m_size;
+};
+Q_DECLARE_TYPEINFO(QAnyStringView, Q_PRIMITIVE_TYPE);
+
+template <typename QStringLike, std::enable_if_t<std::disjunction_v<
+ std::is_same<QStringLike, QString>,
+ std::is_same<QStringLike, QByteArray>
+ >, bool> = true>
+[[nodiscard]] inline QAnyStringView qToAnyStringViewIgnoringNull(const QStringLike &s) noexcept
+{ return QAnyStringView(s.data(), s.size()); }
+
+
+#define Q_ANY_SV_MAKE_RELOP(op) \
+ [[nodiscard]] Q_ALWAYS_INLINE auto operator op (QAnyStringView lhs, QAnyStringView rhs) noexcept \
+ { return QAnyStringView::compare(lhs, rhs) op 0; } \
+ /* end */
+
+Q_ANY_SV_MAKE_RELOP(==) // size() shortcut doesn't apply for UTF-8 vs. {L1, UTF-16}
+Q_ANY_SV_MAKE_RELOP(!=)
+
+#ifdef __cpp_impl_three_way_comparison
+Q_ANY_SV_MAKE_RELOP(<=>)
+#else
+Q_ANY_SV_MAKE_RELOP(<=)
+Q_ANY_SV_MAKE_RELOP(>=)
+Q_ANY_SV_MAKE_RELOP(<)
+Q_ANY_SV_MAKE_RELOP(>)
+#endif
+
+#undef Q_ANY_SV_MAKE_RELOP
+
+QT_END_NAMESPACE
+
+#endif /* QANYSTRINGVIEW_H */