1/****************************************************************************
2**
3** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39#ifndef QANYSTRINGVIEW_H
40#define QANYSTRINGVIEW_H
41
42#include <QtCore/qstringview.h>
43#include <QtCore/qutf8stringview.h>
44
45#ifdef __cpp_impl_three_way_comparison
46#include <compare>
47#endif
48
49QT_BEGIN_NAMESPACE
50
51template <typename, typename> class QStringBuilder;
52
53class QAnyStringView
54{
55public:
56 typedef qptrdiff difference_type;
57 typedef qsizetype size_type;
58private:
59 template <typename Char>
60 using if_compatible_char = std::enable_if_t<std::disjunction_v<
61 QtPrivate::IsCompatibleCharType<Char>,
62 QtPrivate::IsCompatibleChar8Type<Char>
63 >, bool>;
64
65 template <typename Pointer>
66 using if_compatible_pointer = std::enable_if_t<std::disjunction_v<
67 QtPrivate::IsCompatiblePointer<Pointer>,
68 QtPrivate::IsCompatiblePointer8<Pointer>
69 >, bool>;
70
71
72 template <typename T>
73 using if_compatible_container = std::enable_if_t<std::disjunction_v<
74 QtPrivate::IsContainerCompatibleWithQStringView<T>,
75 QtPrivate::IsContainerCompatibleWithQUtf8StringView<T>
76 >, bool>;
77
78 // confirm we don't make an accidental copy constructor:
79 static_assert(QtPrivate::IsContainerCompatibleWithQStringView<QAnyStringView>::value == false);
80 static_assert(QtPrivate::IsContainerCompatibleWithQUtf8StringView<QAnyStringView>::value == false);
81
82 template <typename Char>
83 static constexpr std::size_t encodeType(qsizetype sz) noexcept
84 {
85 // only deals with Utf8 and Utf16 - there's only one way to create
86 // a Latin1 string, and that ctor deals with the tag itself
87 Q_ASSERT(sz >= 0);
88 Q_ASSERT(sz <= qsizetype(SizeMask));
89 return std::size_t(sz) | uint(sizeof(Char) == sizeof(char16_t)) * Tag::Utf16;
90 }
91
92 template <typename Char>
93 static qsizetype lengthHelperPointer(const Char *str) noexcept
94 {
95#if defined(Q_CC_GNU) && !defined(Q_CC_CLANG) && !defined(Q_CC_INTEL)
96 if (__builtin_constant_p(*str)) {
97 qsizetype result = 0;
98 while (*str++ != u'\0')
99 ++result;
100 return result;
101 }
102#endif
103 if constexpr (sizeof(Char) == sizeof(char16_t))
104 return QtPrivate::qustrlen(reinterpret_cast<const char16_t*>(str));
105 else
106 return qsizetype(strlen(reinterpret_cast<const char*>(str)));
107 }
108
109 template <typename Container>
110 static constexpr qsizetype lengthHelperContainer(const Container &c) noexcept
111 {
112 return qsizetype(std::size(c));
113 }
114
115 template <typename Char, size_t N>
116 static constexpr qsizetype lengthHelperContainer(const Char (&str)[N]) noexcept
117 {
118 const auto it = std::char_traits<Char>::find(str, N, Char(0));
119 const auto end = it ? it : std::next(str, N);
120 return qsizetype(std::distance(str, end));
121 }
122
123 static QChar toQChar(char ch) noexcept { return toQChar(QLatin1Char{ch}); } // we don't handle UTF-8 multibytes
124 static QChar toQChar(QChar ch) noexcept { return ch; }
125 static QChar toQChar(QLatin1Char ch) noexcept { return ch; }
126
127 explicit constexpr QAnyStringView(const void *d, qsizetype n, std::size_t sizeAndType) noexcept
128 : m_data{d}, m_size{std::size_t(n) | (sizeAndType & TypeMask)} {}
129public:
130 constexpr QAnyStringView() noexcept
131 : m_data{nullptr}, m_size{0} {}
132 constexpr QAnyStringView(std::nullptr_t) noexcept
133 : QAnyStringView() {}
134
135 template <typename Char, if_compatible_char<Char> = true>
136 constexpr QAnyStringView(const Char *str, qsizetype len)
137 : m_data{str},
138 m_size{encodeType<Char>((Q_ASSERT(len >= 0), Q_ASSERT(str || !len), len))} {}
139
140 template <typename Char, if_compatible_char<Char> = true>
141 constexpr QAnyStringView(const Char *f, const Char *l)
142 : QAnyStringView(f, l - f) {}
143
144#ifdef Q_CLANG_QDOC
145 template <typename Char, size_t N>
146 constexpr QAnyStringView(const Char (&array)[N]) noexcept;
147
148 template <typename Char>
149 constexpr QAnyStringView(const Char *str) noexcept;
150#else
151
152 template <typename Pointer, if_compatible_pointer<Pointer> = true>
153 constexpr QAnyStringView(const Pointer &str) noexcept
154 : QAnyStringView{str, str ? lengthHelperPointer(str) : 0} {}
155#endif
156
157 // defined in qstring.h
158 inline QAnyStringView(const QByteArray &str) noexcept; // TODO: Should we have this at all? Remove?
159 inline QAnyStringView(const QString &str) noexcept;
160 inline constexpr QAnyStringView(QLatin1String str) noexcept;
161
162 // defined in qstringbuilder.h
163 template <typename A, typename B>
164 inline QAnyStringView(const QStringBuilder<A, B> &expr,
165 typename QStringBuilder<A, B>::ConvertTo &&capacity = {});
166
167 template <typename Container, if_compatible_container<Container> = true>
168 constexpr QAnyStringView(const Container &c) noexcept
169 : QAnyStringView(std::data(c), lengthHelperContainer(c)) {}
170
171 template <typename Char, if_compatible_char<Char> = true>
172 constexpr QAnyStringView(const Char &c) noexcept
173 : QAnyStringView{&c, 1} {}
174 constexpr QAnyStringView(const QChar &c) noexcept
175 : QAnyStringView{&c, 1} {}
176
177 template <typename Char, typename Container = decltype(QChar::fromUcs4(U'x')),
178 std::enable_if_t<std::is_same_v<Char, char32_t>, bool> = true>
179 constexpr QAnyStringView(Char c, Container &&capacity = {})
180 : QAnyStringView(capacity = QChar::fromUcs4(c)) {}
181
182 constexpr QAnyStringView(QStringView v) noexcept
183 : QAnyStringView(std::data(v), lengthHelperContainer(v)) {}
184
185 template <bool UseChar8T>
186 constexpr QAnyStringView(QBasicUtf8StringView<UseChar8T> v) noexcept
187 : QAnyStringView(std::data(v), lengthHelperContainer(v)) {}
188
189 template <typename Char, size_t Size, if_compatible_char<Char> = true>
190 [[nodiscard]] constexpr static QAnyStringView fromArray(const Char (&string)[Size]) noexcept
191 { return QAnyStringView(string, Size); }
192
193 // defined in qstring.h:
194 template <typename Visitor>
195 inline constexpr decltype(auto) visit(Visitor &&v) const;
196
197 [[nodiscard]] inline QString toString() const; // defined in qstring.h
198
199 [[nodiscard]] constexpr qsizetype size() const noexcept { return qsizetype(m_size & SizeMask); }
200 [[nodiscard]] constexpr const void *data() const noexcept { return m_data; }
201
202 [[nodiscard]] Q_CORE_EXPORT static int compare(QAnyStringView lhs, QAnyStringView rhs, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
203 [[nodiscard]] Q_CORE_EXPORT static bool equal(QAnyStringView lhs, QAnyStringView rhs) noexcept;
204
205 //
206 // STL compatibility API:
207 //
208 [[nodiscard]] constexpr QChar front() const; // NOT noexcept!
209 [[nodiscard]] constexpr QChar back() const; // NOT noexcept!
210 [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
211 [[nodiscard]] constexpr qsizetype size_bytes() const noexcept
212 { return size() * charSize(); }
213
214 //
215 // Qt compatibility API:
216 //
217 [[nodiscard]] constexpr bool isNull() const noexcept { return !m_data; }
218 [[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); }
219#if QT_DEPRECATED_SINCE(6, 0)
220 [[nodiscard]]
221 Q_DECL_DEPRECATED_X("Use size() and port callers to qsizetype.")
222 constexpr int length() const /* not nothrow! */
223 { return Q_ASSERT(int(size()) == size()), int(size()); }
224#endif
225
226private:
227 [[nodiscard]] friend inline bool operator==(QAnyStringView lhs, QAnyStringView rhs) noexcept
228 { return QAnyStringView::equal(lhs, rhs); }
229 [[nodiscard]] friend inline bool operator!=(QAnyStringView lhs, QAnyStringView rhs) noexcept
230 { return !QAnyStringView::equal(lhs, rhs); }
231
232#ifdef __cpp_impl_three_way_comparison
233 [[nodiscard]] friend inline auto operator<=>(QAnyStringView lhs, QAnyStringView rhs) noexcept
234 { return QAnyStringView::compare(lhs, rhs) <=> 0; }
235#else
236 [[nodiscard]] friend inline bool operator<=(QAnyStringView lhs, QAnyStringView rhs) noexcept
237 { return QAnyStringView::compare(lhs, rhs) <= 0; }
238 [[nodiscard]] friend inline bool operator>=(QAnyStringView lhs, QAnyStringView rhs) noexcept
239 { return QAnyStringView::compare(lhs, rhs) >= 0; }
240 [[nodiscard]] friend inline bool operator<(QAnyStringView lhs, QAnyStringView rhs) noexcept
241 { return QAnyStringView::compare(lhs, rhs) < 0; }
242 [[nodiscard]] friend inline bool operator>(QAnyStringView lhs, QAnyStringView rhs) noexcept
243 { return QAnyStringView::compare(lhs, rhs) > 0; }
244#endif
245
246 // TODO: Optimize by inverting and storing the flags in the low bits and
247 // the size in the high.
248 static_assert(std::is_same_v<std::size_t, size_t>);
249 static_assert(sizeof(size_t) == sizeof(qsizetype));
250 static constexpr size_t SizeMask = (std::numeric_limits<size_t>::max)() / 4;
251 static constexpr size_t Latin1Flag = SizeMask + 1;
252 static constexpr size_t TwoByteCodePointFlag = Latin1Flag << 1;
253 static constexpr size_t TypeMask = (std::numeric_limits<size_t>::max)() & ~SizeMask;
254 static_assert(TypeMask == (Latin1Flag|TwoByteCodePointFlag));
255 // HI HI LO LO ...
256 // 0 0 SZ SZ Utf8
257 // 0 1 SZ SZ Latin1
258 // 1 0 SZ SZ Utf16
259 // 1 1 SZ SZ Unused
260 // ^ ^ latin1
261 // | sizeof code-point == 2
262 enum Tag : size_t {
263 Utf8 = 0,
264 Latin1 = Latin1Flag,
265 Utf16 = TwoByteCodePointFlag,
266 Unused = TypeMask,
267 };
268 [[nodiscard]] constexpr Tag tag() const noexcept { return Tag{m_size & TypeMask}; }
269 [[nodiscard]] constexpr bool isUtf16() const noexcept { return tag() == Tag::Utf16; }
270 [[nodiscard]] constexpr bool isUtf8() const noexcept { return tag() == Tag::Utf8; }
271 [[nodiscard]] constexpr bool isLatin1() const noexcept { return tag() == Tag::Latin1; }
272 [[nodiscard]] constexpr QStringView asStringView() const
273 { return Q_ASSERT(isUtf16()), QStringView{m_data_utf16, size()}; }
274 [[nodiscard]] constexpr q_no_char8_t::QUtf8StringView asUtf8StringView() const
275 { return Q_ASSERT(isUtf8()), q_no_char8_t::QUtf8StringView{m_data_utf8, size()}; }
276 [[nodiscard]] inline constexpr QLatin1String asLatin1StringView() const;
277 [[nodiscard]] constexpr size_t charSize() const noexcept { return isUtf16() ? 2 : 1; }
278 Q_ALWAYS_INLINE constexpr void verify(qsizetype pos, qsizetype n = 0) const
279 {
280 Q_ASSERT(pos >= 0);
281 Q_ASSERT(pos <= size());
282 Q_ASSERT(n >= 0);
283 Q_ASSERT(n <= size() - pos);
284 }
285 union {
286 const void *m_data;
287 const char *m_data_utf8;
288 const char16_t *m_data_utf16;
289 };
290 size_t m_size;
291};
292Q_DECLARE_TYPEINFO(QAnyStringView, Q_PRIMITIVE_TYPE);
293
294template <typename QStringLike, std::enable_if_t<std::disjunction_v<
295 std::is_same<QStringLike, QString>,
296 std::is_same<QStringLike, QByteArray>
297 >, bool> = true>
298[[nodiscard]] inline QAnyStringView qToAnyStringViewIgnoringNull(const QStringLike &s) noexcept
299{ return QAnyStringView(s.data(), s.size()); }
300
301QT_END_NAMESPACE
302
303#endif /* QANYSTRINGVIEW_H */
304