1 | // Copyright (C) 2021 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | #ifndef QBYTEARRAYVIEW_H |
4 | #define QBYTEARRAYVIEW_H |
5 | |
6 | #include <QtCore/qbytearrayalgorithms.h> |
7 | #include <QtCore/qstringfwd.h> |
8 | #include <QtCore/qarraydata.h> |
9 | |
10 | #include <string> |
11 | #include <string_view> |
12 | #include <QtCore/q20type_traits.h> |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | namespace QtPrivate { |
17 | |
18 | template <typename Byte> |
19 | struct IsCompatibleByteTypeHelper |
20 | : std::integral_constant<bool, |
21 | std::is_same_v<Byte, char> || |
22 | std::is_same_v<Byte, uchar> || |
23 | std::is_same_v<Byte, signed char> || |
24 | std::is_same_v<Byte, std::byte>> {}; |
25 | |
26 | template <typename Byte> |
27 | struct IsCompatibleByteType |
28 | : IsCompatibleByteTypeHelper<q20::remove_cvref_t<Byte>> {}; |
29 | |
30 | template <typename Pointer> |
31 | struct IsCompatibleByteArrayPointerHelper : std::false_type {}; |
32 | template <typename Byte> |
33 | struct IsCompatibleByteArrayPointerHelper<Byte *> |
34 | : IsCompatibleByteType<Byte> {}; |
35 | template<typename Pointer> |
36 | struct IsCompatibleByteArrayPointer |
37 | : IsCompatibleByteArrayPointerHelper<q20::remove_cvref_t<Pointer>> {}; |
38 | |
39 | template <typename T, typename Enable = void> |
40 | struct IsContainerCompatibleWithQByteArrayView : std::false_type {}; |
41 | |
42 | template <typename T> |
43 | struct IsContainerCompatibleWithQByteArrayView<T, std::enable_if_t< |
44 | std::conjunction_v< |
45 | // lacking concepts and ranges, we accept any T whose std::data yields a suitable |
46 | // pointer ... |
47 | IsCompatibleByteArrayPointer<decltype(std::data(std::declval<const T &>()))>, |
48 | // ... and that has a suitable size ... |
49 | std::is_convertible<decltype(std::size(std::declval<const T &>())), qsizetype>, |
50 | // ... and it's a range as it defines an iterator-like API |
51 | IsCompatibleByteType<typename std::iterator_traits<decltype( |
52 | std::begin(std::declval<const T &>()))>::value_type>, |
53 | std::is_convertible<decltype(std::begin(std::declval<const T &>()) |
54 | != std::end(std::declval<const T &>())), |
55 | bool>, |
56 | |
57 | // This needs to be treated specially due to the empty vs null distinction |
58 | std::negation<std::is_same<std::decay_t<T>, QByteArray>>, |
59 | |
60 | // We handle array literals specially for source compat reasons |
61 | std::negation<std::is_array<T>>, |
62 | |
63 | // Don't make an accidental copy constructor |
64 | std::negation<std::is_same<std::decay_t<T>, QByteArrayView>>>>> : std::true_type {}; |
65 | |
66 | // Used by QLatin1StringView too |
67 | template <typename Char> |
68 | static constexpr qsizetype lengthHelperPointer(const Char *data) noexcept |
69 | { |
70 | return qsizetype(std::char_traits<Char>::length(data)); |
71 | } |
72 | |
73 | } // namespace QtPrivate |
74 | |
75 | class Q_CORE_EXPORT QByteArrayView |
76 | { |
77 | public: |
78 | typedef char storage_type; |
79 | typedef const char value_type; |
80 | typedef qptrdiff difference_type; |
81 | typedef qsizetype size_type; |
82 | typedef value_type &reference; |
83 | typedef value_type &const_reference; |
84 | typedef value_type *pointer; |
85 | typedef value_type *const_pointer; |
86 | |
87 | typedef pointer iterator; |
88 | typedef const_pointer const_iterator; |
89 | typedef std::reverse_iterator<iterator> reverse_iterator; |
90 | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
91 | |
92 | private: |
93 | template <typename Byte> |
94 | using if_compatible_byte = |
95 | typename std::enable_if_t<QtPrivate::IsCompatibleByteType<Byte>::value, bool>; |
96 | |
97 | template <typename Pointer> |
98 | using if_compatible_pointer = |
99 | typename std::enable_if_t<QtPrivate::IsCompatibleByteArrayPointer<Pointer>::value, |
100 | bool>; |
101 | |
102 | template <typename T> |
103 | using if_compatible_qbytearray_like = |
104 | typename std::enable_if_t<std::is_same_v<T, QByteArray>, bool>; |
105 | |
106 | template <typename T> |
107 | using if_compatible_container = |
108 | typename std::enable_if_t<QtPrivate::IsContainerCompatibleWithQByteArrayView<T>::value, |
109 | bool>; |
110 | |
111 | template <typename Container> |
112 | static constexpr qsizetype lengthHelperContainer(const Container &c) noexcept |
113 | { |
114 | return qsizetype(std::size(c)); |
115 | } |
116 | |
117 | static constexpr qsizetype lengthHelperCharArray(const char *data, size_t size) noexcept |
118 | { |
119 | const auto it = std::char_traits<char>::find(s: data, n: size, a: '\0'); |
120 | const auto end = it ? it : std::next(x: data, n: size); |
121 | return qsizetype(std::distance(first: data, last: end)); |
122 | } |
123 | |
124 | template <typename Byte> |
125 | static const storage_type *castHelper(const Byte *data) noexcept |
126 | { return reinterpret_cast<const storage_type*>(data); } |
127 | static constexpr const storage_type *castHelper(const storage_type *data) noexcept |
128 | { return data; } |
129 | |
130 | public: |
131 | constexpr QByteArrayView() noexcept |
132 | : m_size(0), m_data(nullptr) {} |
133 | constexpr QByteArrayView(std::nullptr_t) noexcept |
134 | : QByteArrayView() {} |
135 | |
136 | template <typename Byte, if_compatible_byte<Byte> = true> |
137 | constexpr QByteArrayView(const Byte *data, qsizetype len) |
138 | : m_size((Q_ASSERT(len >= 0), Q_ASSERT(data || !len), len)), |
139 | m_data(castHelper(data)) {} |
140 | |
141 | template <typename Byte, if_compatible_byte<Byte> = true> |
142 | constexpr QByteArrayView(const Byte *first, const Byte *last) |
143 | : QByteArrayView(first, last - first) {} |
144 | |
145 | #ifdef Q_QDOC |
146 | template <typename Byte> |
147 | constexpr QByteArrayView(const Byte *data) noexcept; |
148 | #else |
149 | template <typename Pointer, if_compatible_pointer<Pointer> = true> |
150 | constexpr QByteArrayView(const Pointer &data) noexcept |
151 | : QByteArrayView( |
152 | data, data ? QtPrivate::lengthHelperPointer(data) : 0) {} |
153 | #endif |
154 | |
155 | #ifdef Q_QDOC |
156 | QByteArrayView(const QByteArray &data) noexcept; |
157 | #else |
158 | template <typename ByteArray, if_compatible_qbytearray_like<ByteArray> = true> |
159 | QByteArrayView(const ByteArray &ba) noexcept |
160 | : QByteArrayView(ba.isNull() ? nullptr : ba.data(), qsizetype(ba.size())) {} |
161 | #endif |
162 | |
163 | template <typename Container, if_compatible_container<Container> = true> |
164 | constexpr QByteArrayView(const Container &c) noexcept |
165 | : QByteArrayView(std::data(c), lengthHelperContainer(c)) {} |
166 | template <size_t Size> |
167 | constexpr QByteArrayView(const char (&data)[Size]) noexcept |
168 | : QByteArrayView(data, lengthHelperCharArray(data, size: Size)) {} |
169 | |
170 | #ifdef Q_QDOC |
171 | template <typename Byte, size_t Size> |
172 | #else |
173 | template <typename Byte, size_t Size, if_compatible_byte<Byte> = true> |
174 | #endif |
175 | [[nodiscard]] constexpr static QByteArrayView fromArray(const Byte (&data)[Size]) noexcept |
176 | { return QByteArrayView(data, Size); } |
177 | [[nodiscard]] inline QByteArray toByteArray() const; // defined in qbytearray.h |
178 | |
179 | [[nodiscard]] constexpr qsizetype size() const noexcept { return m_size; } |
180 | [[nodiscard]] constexpr const_pointer data() const noexcept { return m_data; } |
181 | [[nodiscard]] constexpr const_pointer constData() const noexcept { return data(); } |
182 | |
183 | [[nodiscard]] constexpr char operator[](qsizetype n) const |
184 | { verify(pos: n, n: 1); return m_data[n]; } |
185 | |
186 | // |
187 | // QByteArray API |
188 | // |
189 | [[nodiscard]] constexpr char at(qsizetype n) const { return (*this)[n]; } |
190 | |
191 | [[nodiscard]] constexpr QByteArrayView first(qsizetype n) const |
192 | { verify(pos: 0, n); return sliced(pos: 0, n); } |
193 | [[nodiscard]] constexpr QByteArrayView last(qsizetype n) const |
194 | { verify(pos: 0, n); return sliced(pos: size() - n, n); } |
195 | [[nodiscard]] constexpr QByteArrayView sliced(qsizetype pos) const |
196 | { verify(pos, n: 0); return QByteArrayView(data() + pos, size() - pos); } |
197 | [[nodiscard]] constexpr QByteArrayView sliced(qsizetype pos, qsizetype n) const |
198 | { verify(pos, n); return QByteArrayView(data() + pos, n); } |
199 | [[nodiscard]] constexpr QByteArrayView chopped(qsizetype len) const |
200 | { verify(pos: 0, n: len); return sliced(pos: 0, n: size() - len); } |
201 | |
202 | [[nodiscard]] constexpr QByteArrayView left(qsizetype n) const |
203 | { if (n < 0 || n > size()) n = size(); return QByteArrayView(data(), n); } |
204 | [[nodiscard]] constexpr QByteArrayView right(qsizetype n) const |
205 | { if (n < 0 || n > size()) n = size(); if (n < 0) n = 0; return QByteArrayView(data() + size() - n, n); } |
206 | [[nodiscard]] constexpr QByteArrayView mid(qsizetype pos, qsizetype n = -1) const |
207 | { |
208 | using namespace QtPrivate; |
209 | auto result = QContainerImplHelper::mid(originalLength: size(), position: &pos, length: &n); |
210 | return result == QContainerImplHelper::Null ? QByteArrayView() |
211 | : QByteArrayView(m_data + pos, n); |
212 | } |
213 | |
214 | constexpr void truncate(qsizetype n) |
215 | { verify(pos: 0, n); m_size = n; } |
216 | constexpr void chop(qsizetype n) |
217 | { verify(pos: 0, n); m_size -= n; } |
218 | |
219 | // Defined in qbytearray.cpp: |
220 | [[nodiscard]] QByteArrayView trimmed() const noexcept |
221 | { return QtPrivate::trimmed(s: *this); } |
222 | [[nodiscard]] short toShort(bool *ok = nullptr, int base = 10) const |
223 | { return QtPrivate::toIntegral<short>(data: *this, ok, base); } |
224 | [[nodiscard]] ushort toUShort(bool *ok = nullptr, int base = 10) const |
225 | { return QtPrivate::toIntegral<ushort>(data: *this, ok, base); } |
226 | [[nodiscard]] int toInt(bool *ok = nullptr, int base = 10) const |
227 | { return QtPrivate::toIntegral<int>(data: *this, ok, base); } |
228 | [[nodiscard]] uint toUInt(bool *ok = nullptr, int base = 10) const |
229 | { return QtPrivate::toIntegral<uint>(data: *this, ok, base); } |
230 | [[nodiscard]] long toLong(bool *ok = nullptr, int base = 10) const |
231 | { return QtPrivate::toIntegral<long>(data: *this, ok, base); } |
232 | [[nodiscard]] ulong toULong(bool *ok = nullptr, int base = 10) const |
233 | { return QtPrivate::toIntegral<ulong>(data: *this, ok, base); } |
234 | [[nodiscard]] qlonglong toLongLong(bool *ok = nullptr, int base = 10) const |
235 | { return QtPrivate::toIntegral<qlonglong>(data: *this, ok, base); } |
236 | [[nodiscard]] qulonglong toULongLong(bool *ok = nullptr, int base = 10) const |
237 | { return QtPrivate::toIntegral<qulonglong>(data: *this, ok, base); } |
238 | [[nodiscard]] float toFloat(bool *ok = nullptr) const |
239 | { |
240 | const auto r = QtPrivate::toFloat(a: *this); |
241 | if (ok) |
242 | *ok = bool(r); |
243 | return r.value_or(u: 0.0f); |
244 | } |
245 | [[nodiscard]] double toDouble(bool *ok = nullptr) const |
246 | { |
247 | const auto r = QtPrivate::toDouble(a: *this); |
248 | if (ok) |
249 | *ok = bool(r); |
250 | return r.value_or(u: 0.0); |
251 | } |
252 | |
253 | [[nodiscard]] bool startsWith(QByteArrayView other) const noexcept |
254 | { return QtPrivate::startsWith(haystack: *this, needle: other); } |
255 | [[nodiscard]] bool startsWith(char c) const noexcept |
256 | { return !empty() && front() == c; } |
257 | |
258 | [[nodiscard]] bool endsWith(QByteArrayView other) const noexcept |
259 | { return QtPrivate::endsWith(haystack: *this, needle: other); } |
260 | [[nodiscard]] bool endsWith(char c) const noexcept |
261 | { return !empty() && back() == c; } |
262 | |
263 | [[nodiscard]] qsizetype indexOf(QByteArrayView a, qsizetype from = 0) const noexcept |
264 | { return QtPrivate::findByteArray(haystack: *this, from, needle: a); } |
265 | [[nodiscard]] qsizetype indexOf(char ch, qsizetype from = 0) const noexcept |
266 | { return QtPrivate::findByteArray(haystack: *this, from, needle: QByteArrayView(&ch, 1)); } |
267 | |
268 | [[nodiscard]] bool contains(QByteArrayView a) const noexcept |
269 | { return indexOf(a) != qsizetype(-1); } |
270 | [[nodiscard]] bool contains(char c) const noexcept |
271 | { return indexOf(ch: c) != qsizetype(-1); } |
272 | |
273 | [[nodiscard]] qsizetype lastIndexOf(QByteArrayView a) const noexcept |
274 | { return lastIndexOf(a, from: size()); } |
275 | [[nodiscard]] qsizetype lastIndexOf(QByteArrayView a, qsizetype from) const noexcept |
276 | { return QtPrivate::lastIndexOf(haystack: *this, from, needle: a); } |
277 | [[nodiscard]] qsizetype lastIndexOf(char ch, qsizetype from = -1) const noexcept |
278 | { return QtPrivate::lastIndexOf(haystack: *this, from, needle: QByteArrayView(&ch, 1)); } |
279 | |
280 | [[nodiscard]] qsizetype count(QByteArrayView a) const noexcept |
281 | { return QtPrivate::count(haystack: *this, needle: a); } |
282 | [[nodiscard]] qsizetype count(char ch) const noexcept |
283 | { return QtPrivate::count(haystack: *this, needle: QByteArrayView(&ch, 1)); } |
284 | |
285 | inline int compare(QByteArrayView a, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept; |
286 | |
287 | [[nodiscard]] inline bool isValidUtf8() const noexcept { return QtPrivate::isValidUtf8(s: *this); } |
288 | |
289 | // |
290 | // STL compatibility API: |
291 | // |
292 | [[nodiscard]] constexpr const_iterator begin() const noexcept { return data(); } |
293 | [[nodiscard]] constexpr const_iterator end() const noexcept { return data() + size(); } |
294 | [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } |
295 | [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } |
296 | [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } |
297 | [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } |
298 | [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } |
299 | [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } |
300 | |
301 | [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } |
302 | [[nodiscard]] constexpr char front() const { Q_ASSERT(!empty()); return m_data[0]; } |
303 | [[nodiscard]] constexpr char back() const { Q_ASSERT(!empty()); return m_data[m_size - 1]; } |
304 | |
305 | [[nodiscard]] constexpr Q_IMPLICIT operator std::string_view() const noexcept |
306 | { return std::string_view(m_data, size_t(m_size)); } |
307 | |
308 | // |
309 | // Qt compatibility API: |
310 | // |
311 | [[nodiscard]] constexpr bool isNull() const noexcept { return !m_data; } |
312 | [[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); } |
313 | [[nodiscard]] constexpr qsizetype length() const noexcept |
314 | { return size(); } |
315 | [[nodiscard]] constexpr char first() const { return front(); } |
316 | [[nodiscard]] constexpr char last() const { return back(); } |
317 | |
318 | friend inline bool operator==(QByteArrayView lhs, QByteArrayView rhs) noexcept |
319 | { return lhs.size() == rhs.size() && (!lhs.size() || memcmp(s1: lhs.data(), s2: rhs.data(), n: lhs.size()) == 0); } |
320 | friend inline bool operator!=(QByteArrayView lhs, QByteArrayView rhs) noexcept |
321 | { return !(lhs == rhs); } |
322 | friend inline bool operator< (QByteArrayView lhs, QByteArrayView rhs) noexcept |
323 | { return QtPrivate::compareMemory(lhs, rhs) < 0; } |
324 | friend inline bool operator<=(QByteArrayView lhs, QByteArrayView rhs) noexcept |
325 | { return QtPrivate::compareMemory(lhs, rhs) <= 0; } |
326 | friend inline bool operator> (QByteArrayView lhs, QByteArrayView rhs) noexcept |
327 | { return !(lhs <= rhs); } |
328 | friend inline bool operator>=(QByteArrayView lhs, QByteArrayView rhs) noexcept |
329 | { return !(lhs < rhs); } |
330 | |
331 | private: |
332 | Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0, |
333 | [[maybe_unused]] qsizetype n = 1) const |
334 | { |
335 | Q_ASSERT(pos >= 0); |
336 | Q_ASSERT(pos <= size()); |
337 | Q_ASSERT(n >= 0); |
338 | Q_ASSERT(n <= size() - pos); |
339 | } |
340 | |
341 | qsizetype m_size; |
342 | const storage_type *m_data; |
343 | }; |
344 | Q_DECLARE_TYPEINFO(QByteArrayView, Q_PRIMITIVE_TYPE); |
345 | |
346 | template<typename QByteArrayLike, |
347 | std::enable_if_t<std::is_same_v<QByteArrayLike, QByteArray>, bool> = true> |
348 | [[nodiscard]] inline QByteArrayView qToByteArrayViewIgnoringNull(const QByteArrayLike &b) noexcept |
349 | { return QByteArrayView(b.data(), b.size()); } |
350 | |
351 | inline int QByteArrayView::compare(QByteArrayView a, Qt::CaseSensitivity cs) const noexcept |
352 | { |
353 | return cs == Qt::CaseSensitive ? QtPrivate::compareMemory(lhs: *this, rhs: a) : |
354 | qstrnicmp(data(), size(), a.data(), a.size()); |
355 | } |
356 | |
357 | #if QT_DEPRECATED_SINCE(6, 0) |
358 | QT_DEPRECATED_VERSION_X_6_0("Use the QByteArrayView overload." ) |
359 | inline quint16 qChecksum(const char *s, qsizetype len, |
360 | Qt::ChecksumType standard = Qt::ChecksumIso3309) |
361 | { return qChecksum(data: QByteArrayView(s, len), standard); } |
362 | #endif |
363 | |
364 | QT_END_NAMESPACE |
365 | |
366 | #endif // QBYTEARRAYVIEW_H |
367 | |