1// Copyright (C) 2022 The Qt Company Ltd.
2// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4#ifndef QANYSTRINGVIEW_H
5#define QANYSTRINGVIEW_H
6
7#include <QtCore/qcompare.h>
8#include <QtCore/qcontainerfwd.h>
9#include <QtCore/qlatin1stringview.h>
10#include <QtCore/qstringview.h>
11#include <QtCore/qutf8stringview.h>
12
13#include <QtCore/q20type_traits.h>
14#include <limits>
15
16class tst_QAnyStringView;
17
18QT_BEGIN_NAMESPACE
19
20namespace QtPrivate {
21
22template <typename Tag, typename Result>
23struct wrapped { using type = Result; };
24
25template <typename Tag, typename Result>
26using wrapped_t = typename wrapped<Tag, Result>::type;
27
28} // namespace QtPrivate
29
30class QAnyStringView
31{
32public:
33 typedef qptrdiff difference_type;
34 typedef qsizetype size_type;
35private:
36 static constexpr size_t SizeMask = (std::numeric_limits<size_t>::max)() / 4;
37#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED)
38 static constexpr int SizeShift = 2;
39 static constexpr size_t Latin1Flag = 1;
40#else
41 static constexpr int SizeShift = 0;
42 static constexpr size_t Latin1Flag = SizeMask + 1;
43#endif
44 static constexpr size_t TwoByteCodePointFlag = Latin1Flag << 1;
45 static constexpr size_t TypeMask = ~(SizeMask << SizeShift);
46 static_assert(TypeMask == (Latin1Flag|TwoByteCodePointFlag));
47
48 // Tag bits
49 // 0 0 Utf8
50 // 0 1 Latin1
51 // 1 0 Utf16
52 // 1 1 Unused
53 // ^ ^ latin1
54 // | sizeof code-point == 2
55 enum Tag : size_t {
56 Utf8 = 0,
57 Latin1 = Latin1Flag,
58 Utf16 = TwoByteCodePointFlag,
59 Unused = TypeMask,
60 };
61
62 template <typename Char>
63 using is_compatible_char = std::disjunction<
64 QtPrivate::IsCompatibleCharType<Char>,
65 QtPrivate::IsCompatibleChar8Type<Char>
66 >;
67
68 template <typename Char>
69 using if_compatible_char = std::enable_if_t<is_compatible_char<Char>::value, bool>;
70
71 template <typename Pointer>
72 using if_compatible_pointer = std::enable_if_t<std::disjunction_v<
73 QtPrivate::IsCompatiblePointer<Pointer>,
74 QtPrivate::IsCompatiblePointer8<Pointer>
75 >, bool>;
76
77
78 template <typename T>
79 using if_compatible_container = std::enable_if_t<std::disjunction_v<
80 QtPrivate::IsContainerCompatibleWithQStringView<T>,
81 QtPrivate::IsContainerCompatibleWithQUtf8StringView<T>
82 >, bool>;
83
84 template <typename QStringOrQByteArray, typename T>
85 using if_convertible_to = std::enable_if_t<std::conjunction_v<
86 // need to exclude a bunch of stuff, because we take by universal reference:
87 std::negation<std::disjunction<
88 std::is_same<q20::remove_cvref_t<T>, QAnyStringView::Tag>,
89 std::is_same<q20::remove_cvref_t<T>, QAnyStringView>, // don't make a copy/move ctor
90 std::is_pointer<std::decay_t<T>>, // const char*, etc
91 is_compatible_char<T>, // don't create a QString/QByteArray, we have a ctor
92 std::is_same<q20::remove_cvref_t<T>, QByteArray>,
93 std::is_same<q20::remove_cvref_t<T>, QString>
94 >>,
95 // this is what we're really after:
96 std::is_convertible<T, QStringOrQByteArray>
97 >, bool>;
98
99 // confirm we don't make an accidental copy constructor:
100 static_assert(QtPrivate::IsContainerCompatibleWithQStringView<QAnyStringView>::value == false);
101 static_assert(QtPrivate::IsContainerCompatibleWithQUtf8StringView<QAnyStringView>::value == false);
102
103 template<typename Char>
104 static constexpr bool isAsciiOnlyCharsAtCompileTime(Char *str, qsizetype sz) noexcept
105 {
106 // do not perform check if not at compile time
107 if (!q20::is_constant_evaluated())
108 return false;
109 if constexpr (sizeof(Char) != sizeof(char)) {
110 Q_UNUSED(str);
111 Q_UNUSED(sz);
112 return false;
113 } else {
114 for (qsizetype i = 0; i < sz; ++i) {
115 if (uchar(str[i]) > 0x7f)
116 return false;
117 }
118 return true;
119 }
120 }
121
122 template<typename Char>
123 static constexpr std::size_t encodeType(const Char *str, qsizetype sz) noexcept
124 {
125 // Utf16 if 16 bit, Latin1 if ASCII, else Utf8
126 Q_ASSERT(sz >= 0);
127 Q_ASSERT(sz <= qsizetype(SizeMask));
128 Q_ASSERT(str || !sz);
129 return (std::size_t(sz) << SizeShift)
130 | uint(sizeof(Char) == sizeof(char16_t)) * Tag::Utf16
131 | uint(isAsciiOnlyCharsAtCompileTime(str, sz)) * Tag::Latin1;
132 }
133
134 template <typename Char>
135 static constexpr qsizetype lengthHelperPointer(const Char *str) noexcept
136 {
137 if (q20::is_constant_evaluated())
138 return QtPrivate::lengthHelperPointer(str);
139 if constexpr (sizeof(Char) == sizeof(char16_t))
140 return QtPrivate::qustrlen(str: reinterpret_cast<const char16_t*>(str));
141 else
142 return qsizetype(strlen(s: reinterpret_cast<const char*>(str)));
143 }
144
145 static QChar toQChar(char ch) noexcept { return toQChar(ch: QLatin1Char{ch}); } // we don't handle UTF-8 multibytes
146 static QChar toQChar(QChar ch) noexcept { return ch; }
147 static QChar toQChar(QLatin1Char ch) noexcept { return ch; }
148
149 struct QCharContainer { // private, so users can't pass their own
150 explicit QCharContainer() = default;
151 QChar ch;
152 };
153
154 explicit constexpr QAnyStringView(const void *d, qsizetype n, std::size_t sizeAndType) noexcept
155 : m_data{d}, m_size{std::size_t(n) | (sizeAndType & TypeMask)} {}
156public:
157 constexpr QAnyStringView() noexcept
158 : m_data{nullptr}, m_size{0} {}
159 constexpr QAnyStringView(std::nullptr_t) noexcept
160 : QAnyStringView() {}
161
162 template <typename Char, if_compatible_char<Char> = true>
163 constexpr QAnyStringView(const Char *str, qsizetype len)
164 : m_data{str}, m_size{encodeType<Char>(str, len)}
165 {
166 }
167
168 template <typename Char, if_compatible_char<Char> = true>
169 constexpr QAnyStringView(const Char *f, const Char *l)
170 : QAnyStringView(f, l - f) {}
171
172#ifdef Q_QDOC
173 template <typename Char, size_t N>
174 constexpr QAnyStringView(const Char (&array)[N]) noexcept;
175
176 template <typename Char>
177 constexpr QAnyStringView(const Char *str) noexcept;
178#else
179 template <typename Pointer, if_compatible_pointer<Pointer> = true>
180 constexpr QAnyStringView(const Pointer &str) noexcept
181 : QAnyStringView{str, str ? lengthHelperPointer(str) : 0} {}
182
183 template <typename Char, if_compatible_char<Char> = true>
184 constexpr QAnyStringView(const Char (&str)[]) noexcept
185 : QAnyStringView{&*str} {} // decay to pointer
186#endif
187
188 // defined in qstring.h
189 inline QAnyStringView(const QByteArray &str) noexcept; // TODO: Should we have this at all? Remove?
190 inline QAnyStringView(const QString &str) noexcept;
191 inline constexpr QAnyStringView(QLatin1StringView str) noexcept;
192
193 template <typename Container, if_compatible_container<Container> = true>
194 constexpr Q_ALWAYS_INLINE QAnyStringView(const Container &c) noexcept
195 : QAnyStringView(std::data(c), QtPrivate::lengthHelperContainer(c)) {}
196
197 template <typename Container, if_convertible_to<QString, Container> = true>
198 constexpr QAnyStringView(Container &&c, QtPrivate::wrapped_t<Container, QString> &&capacity = {})
199 //noexcept(std::is_nothrow_constructible_v<QString, Container>)
200 : QAnyStringView(capacity = std::forward<Container>(c)) {}
201
202 template <typename Container, if_convertible_to<QByteArray, Container> = true>
203 constexpr QAnyStringView(Container &&c, QtPrivate::wrapped_t<Container, QByteArray> &&capacity = {})
204 //noexcept(std::is_nothrow_constructible_v<QByteArray, Container>)
205 : QAnyStringView(capacity = std::forward<Container>(c)) {}
206
207 template <typename Char, if_compatible_char<Char> = true>
208 constexpr QAnyStringView(const Char &c) noexcept
209 : QAnyStringView{&c, 1} {}
210 template <typename Char, if_convertible_to<QChar, Char> = true>
211 constexpr QAnyStringView(Char ch, QCharContainer &&capacity = QCharContainer()) noexcept
212 : QAnyStringView{&(capacity.ch = ch), 1} {}
213 template <typename Char, typename Container = decltype(QChar::fromUcs4(c: U'x')),
214 std::enable_if_t<std::is_same_v<Char, char32_t>, bool> = true>
215 constexpr QAnyStringView(Char c, Container &&capacity = {}) noexcept
216 : QAnyStringView(capacity = QChar::fromUcs4(c)) {}
217
218 constexpr QAnyStringView(QStringView v) noexcept
219 : QAnyStringView(std::data(cont&: v), QtPrivate::lengthHelperContainer(c: v)) {}
220
221 template <bool UseChar8T>
222 constexpr QAnyStringView(QBasicUtf8StringView<UseChar8T> v) noexcept
223 : QAnyStringView(std::data(v), QtPrivate::lengthHelperContainer(v)) {}
224
225 template <typename Char, size_t Size, if_compatible_char<Char> = true>
226 [[nodiscard]] constexpr static QAnyStringView fromArray(const Char (&string)[Size]) noexcept
227 { return QAnyStringView(string, Size); }
228
229 // defined in qstring.h:
230 template <typename Visitor>
231 inline constexpr decltype(auto) visit(Visitor &&v) const;
232
233 [[nodiscard]]
234 constexpr QAnyStringView mid(qsizetype pos, qsizetype n = -1) const
235 {
236 using namespace QtPrivate;
237 auto result = QContainerImplHelper::mid(originalLength: size(), position: &pos, length: &n);
238 return result == QContainerImplHelper::Null ? QAnyStringView() : sliced(pos, n);
239 }
240 [[nodiscard]]
241 constexpr QAnyStringView left(qsizetype n) const
242 {
243 if (size_t(n) >= size_t(size()))
244 n = size();
245 return sliced(pos: 0, n);
246 }
247 [[nodiscard]]
248 constexpr QAnyStringView right(qsizetype n) const
249 {
250 if (size_t(n) >= size_t(size()))
251 n = size();
252 return sliced(pos: size() - n, n);
253 }
254
255 [[nodiscard]] constexpr QAnyStringView sliced(qsizetype pos) const
256 { verify(pos, n: 0); auto r = *this; r.advanceData(delta: pos); r.setSize(size() - pos); return r; }
257 [[nodiscard]] constexpr QAnyStringView sliced(qsizetype pos, qsizetype n) const
258 { verify(pos, n); auto r = *this; r.advanceData(delta: pos); r.setSize(n); return r; }
259 [[nodiscard]] constexpr QAnyStringView first(qsizetype n) const
260 { verify(pos: 0, n); return sliced(pos: 0, n); }
261 [[nodiscard]] constexpr QAnyStringView last(qsizetype n) const
262 { verify(pos: 0, n); return sliced(pos: size() - n, n); }
263 [[nodiscard]] constexpr QAnyStringView chopped(qsizetype n) const
264 { verify(pos: 0, n); return sliced(pos: 0, n: size() - n); }
265
266 constexpr QAnyStringView &slice(qsizetype pos)
267 { *this = sliced(pos); return *this; }
268 constexpr QAnyStringView &slice(qsizetype pos, qsizetype n)
269 { *this = sliced(pos, n); return *this; }
270
271 constexpr void truncate(qsizetype n)
272 { verify(pos: 0, n); setSize(n); }
273 constexpr void chop(qsizetype n)
274 { verify(pos: 0, n); setSize(size() - n); }
275
276
277 [[nodiscard]] inline QString toString() const; // defined in qstring.h
278
279 [[nodiscard]] constexpr qsizetype size() const noexcept
280 { return qsizetype((m_size >> SizeShift) & SizeMask); }
281 [[nodiscard]] constexpr const void *data() const noexcept { return m_data; }
282
283 [[nodiscard]] Q_CORE_EXPORT static int compare(QAnyStringView lhs, QAnyStringView rhs, Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept;
284 [[nodiscard]] Q_CORE_EXPORT static bool equal(QAnyStringView lhs, QAnyStringView rhs) noexcept;
285
286 static constexpr inline bool detects_US_ASCII_at_compile_time =
287#ifdef QT_SUPPORTS_IS_CONSTANT_EVALUATED
288 true
289#else
290 false
291#endif
292 ;
293
294 //
295 // STL compatibility API:
296 //
297 [[nodiscard]] constexpr QChar front() const; // NOT noexcept!
298 [[nodiscard]] constexpr QChar back() const; // NOT noexcept!
299 [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
300 [[nodiscard]] constexpr qsizetype size_bytes() const noexcept
301 { return size() * charSize(); }
302
303 [[nodiscard]] constexpr qsizetype max_size() const noexcept
304 {
305 // -1 to deal with the pointer one-past-the-end;
306 return QtPrivate::MaxAllocSize / charSize() - 1;
307 }
308
309 //
310 // Qt compatibility API:
311 //
312 [[nodiscard]] constexpr bool isNull() const noexcept { return !m_data; }
313 [[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); }
314 [[nodiscard]] constexpr qsizetype length() const noexcept
315 { return size(); }
316
317private:
318 friend bool comparesEqual(const QAnyStringView &lhs, const QAnyStringView &rhs) noexcept
319 { return QAnyStringView::equal(lhs, rhs); }
320 friend Qt::strong_ordering
321 compareThreeWay(const QAnyStringView &lhs, const QAnyStringView &rhs) noexcept
322 {
323 const int res = QAnyStringView::compare(lhs, rhs);
324 return Qt::compareThreeWay(lhs: res, rhs: 0);
325 }
326 Q_DECLARE_STRONGLY_ORDERED(QAnyStringView)
327
328#ifndef QT_NO_DEBUG_STREAM
329 Q_CORE_EXPORT friend QDebug operator<<(QDebug d, QAnyStringView s);
330#endif
331
332 [[nodiscard]] constexpr Tag tag() const noexcept { return Tag{m_size & TypeMask}; }
333 [[nodiscard]] constexpr bool isUtf16() const noexcept { return tag() == Tag::Utf16; }
334 [[nodiscard]] constexpr bool isUtf8() const noexcept { return tag() == Tag::Utf8; }
335 [[nodiscard]] constexpr bool isLatin1() const noexcept { return tag() == Tag::Latin1; }
336 [[nodiscard]] constexpr QStringView asStringView() const
337 { return Q_ASSERT(isUtf16()), QStringView{m_data_utf16, size()}; }
338 [[nodiscard]] constexpr q_no_char8_t::QUtf8StringView asUtf8StringView() const
339 { return Q_ASSERT(isUtf8()), q_no_char8_t::QUtf8StringView{m_data_utf8, size()}; }
340 [[nodiscard]] inline constexpr QLatin1StringView asLatin1StringView() const;
341 [[nodiscard]] constexpr size_t charSize() const noexcept { return isUtf16() ? 2 : 1; }
342 constexpr void setSize(qsizetype sz) noexcept { m_size = size_t(sz) | tag(); }
343 constexpr void advanceData(qsizetype delta) noexcept
344 { m_data_utf8 += delta * charSize(); }
345 Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
346 [[maybe_unused]] qsizetype n = 1) const
347 {
348 Q_ASSERT(pos >= 0);
349 Q_ASSERT(pos <= size());
350 Q_ASSERT(n >= 0);
351 Q_ASSERT(n <= size() - pos);
352 }
353 union {
354 const void *m_data;
355 const char *m_data_utf8;
356 const char16_t *m_data_utf16;
357 };
358 size_t m_size;
359 friend class ::tst_QAnyStringView;
360};
361Q_DECLARE_TYPEINFO(QAnyStringView, Q_PRIMITIVE_TYPE);
362
363template <typename QStringLike, std::enable_if_t<std::disjunction_v<
364 std::is_same<QStringLike, QString>,
365 std::is_same<QStringLike, QByteArray>
366 >, bool> = true>
367[[nodiscard]] inline QAnyStringView qToAnyStringViewIgnoringNull(const QStringLike &s) noexcept
368{ return QAnyStringView(s.begin(), s.size()); }
369
370QT_END_NAMESPACE
371
372#endif /* QANYSTRINGVIEW_H */
373

source code of qtbase/src/corelib/text/qanystringview.h