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