Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qstring.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2022 Intel Corporation.
3// Copyright (C) 2019 Mail.ru Group.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qstringlist.h"
7#if QT_CONFIG(regularexpression)
9#endif
10#include "qunicodetables_p.h"
11#include <private/qstringconverter_p.h>
12#include <private/qtools_p.h>
13#include "qlocale_tools_p.h"
14#include "private/qsimd_p.h"
15#include <qnumeric.h>
16#include <qdatastream.h>
17#include <qlist.h>
18#include "qlocale.h"
19#include "qlocale_p.h"
20#include "qspan.h"
21#include "qstringbuilder.h"
22#include "qstringmatcher.h"
23#include "qvarlengtharray.h"
24#include "qdebug.h"
25#include "qendian.h"
26#include "qcollator.h"
27#include "qttypetraits.h"
28
29#ifdef Q_OS_DARWIN
30#include <private/qcore_mac_p.h>
31#endif
32
33#include <private/qfunctions_p.h>
34
35#include <limits.h>
36#include <string.h>
37#include <stdlib.h>
38#include <stdio.h>
39#include <stdarg.h>
40#include <wchar.h>
41
42#include "qchar.cpp"
44#include "qstringmatcher.cpp"
45#include "qstringiterator_p.h"
46#include "qstringalgorithms_p.h"
47#include "qthreadstorage.h"
48
49#include <algorithm>
50#include <functional>
51
52#ifdef Q_OS_WIN
53# include <qt_windows.h>
54# if !defined(QT_BOOTSTRAPPED) && (defined(QT_NO_CAST_FROM_ASCII) || defined(QT_NO_CAST_TO_ASCII))
55// MSVC requires this, but let's apply it to MinGW compilers too, just in case
56# error "This file cannot be compiled with QT_NO_CAST_{TO,FROM}_ASCII, " \
57 "otherwise some QString functions will not get exported."
58# endif
59#endif
60
61#ifdef truncate
62# undef truncate
63#endif
64
65#define REHASH(a) \
66 if (sl_minus_1 < sizeof(std::size_t) * CHAR_BIT) \
67 hashHaystack -= std::size_t(a) << sl_minus_1; \
68 hashHaystack <<= 1
69
71
72using namespace Qt::StringLiterals;
73using namespace QtMiscUtils;
74
75const char16_t QString::_empty = 0;
76
77// in qstringmatcher.cpp
79
80namespace {
81enum StringComparisonMode {
82 CompareStringsForEquality,
83 CompareStringsForOrdering
84};
85
86template <typename Pointer>
87char32_t foldCaseHelper(Pointer ch, Pointer start) = delete;
88
89template <>
90char32_t foldCaseHelper<const QChar*>(const QChar* ch, const QChar* start)
91{
92 return foldCase(reinterpret_cast<const char16_t*>(ch),
93 reinterpret_cast<const char16_t*>(start));
94}
95
96template <>
97char32_t foldCaseHelper<const char*>(const char* ch, const char*)
98{
99 return foldCase(char16_t(uchar(*ch)));
100}
101
102template <typename T>
103char16_t valueTypeToUtf16(T t) = delete;
104
105template <>
106char16_t valueTypeToUtf16<QChar>(QChar t)
107{
108 return t.unicode();
109}
110
111template <>
112char16_t valueTypeToUtf16<char>(char t)
113{
114 return char16_t{uchar(t)};
115}
116
117template <typename T>
118static inline bool foldAndCompare(const T a, const T b)
119{
120 return foldCase(a) == b;
121}
122
131template <typename Haystack>
132static inline qsizetype qLastIndexOf(Haystack haystack, QChar needle,
133 qsizetype from, Qt::CaseSensitivity cs) noexcept
134{
135 if (haystack.size() == 0)
136 return -1;
137 if (from < 0)
138 from += haystack.size();
139 else if (std::size_t(from) > std::size_t(haystack.size()))
140 from = haystack.size() - 1;
141 if (from >= 0) {
142 char16_t c = needle.unicode();
143 const auto b = haystack.data();
144 auto n = b + from;
145 if (cs == Qt::CaseSensitive) {
146 for (; n >= b; --n)
147 if (valueTypeToUtf16(*n) == c)
148 return n - b;
149 } else {
150 c = foldCase(c);
151 for (; n >= b; --n)
152 if (foldCase(valueTypeToUtf16(*n)) == c)
153 return n - b;
154 }
155 }
156 return -1;
157}
158template <> qsizetype
159qLastIndexOf(QString, QChar, qsizetype, Qt::CaseSensitivity) noexcept = delete; // unwanted, would detach
160
161template<typename Haystack, typename Needle>
162static qsizetype qLastIndexOf(Haystack haystack0, qsizetype from,
163 Needle needle0, Qt::CaseSensitivity cs) noexcept
164{
165 const qsizetype sl = needle0.size();
166 if (sl == 1)
167 return qLastIndexOf(haystack0, needle0.front(), from, cs);
168
169 const qsizetype l = haystack0.size();
170 if (from < 0)
171 from += l;
172 if (from == l && sl == 0)
173 return from;
174 const qsizetype delta = l - sl;
175 if (std::size_t(from) > std::size_t(l) || delta < 0)
176 return -1;
177 if (from > delta)
178 from = delta;
179
180 auto sv = [sl](const typename Haystack::value_type *v) { return Haystack(v, sl); };
181
182 auto haystack = haystack0.data();
183 const auto needle = needle0.data();
184 const auto *end = haystack;
185 haystack += from;
186 const std::size_t sl_minus_1 = sl ? sl - 1 : 0;
187 const auto *n = needle + sl_minus_1;
188 const auto *h = haystack + sl_minus_1;
189 std::size_t hashNeedle = 0, hashHaystack = 0;
190
191 if (cs == Qt::CaseSensitive) {
192 for (qsizetype idx = 0; idx < sl; ++idx) {
193 hashNeedle = (hashNeedle << 1) + valueTypeToUtf16(*(n - idx));
194 hashHaystack = (hashHaystack << 1) + valueTypeToUtf16(*(h - idx));
195 }
196 hashHaystack -= valueTypeToUtf16(*haystack);
197
198 while (haystack >= end) {
199 hashHaystack += valueTypeToUtf16(*haystack);
200 if (hashHaystack == hashNeedle
201 && QtPrivate::compareStrings(needle0, sv(haystack), Qt::CaseSensitive) == 0)
202 return haystack - end;
203 --haystack;
204 REHASH(valueTypeToUtf16(haystack[sl]));
205 }
206 } else {
207 for (qsizetype idx = 0; idx < sl; ++idx) {
208 hashNeedle = (hashNeedle << 1) + foldCaseHelper(n - idx, needle);
209 hashHaystack = (hashHaystack << 1) + foldCaseHelper(h - idx, end);
210 }
211 hashHaystack -= foldCaseHelper(haystack, end);
212
213 while (haystack >= end) {
214 hashHaystack += foldCaseHelper(haystack, end);
215 if (hashHaystack == hashNeedle
216 && QtPrivate::compareStrings(sv(haystack), needle0, Qt::CaseInsensitive) == 0)
217 return haystack - end;
218 --haystack;
219 REHASH(foldCaseHelper(haystack + sl, end));
220 }
221 }
222 return -1;
223}
224
225template <typename Haystack, typename Needle>
226bool qt_starts_with_impl(Haystack haystack, Needle needle, Qt::CaseSensitivity cs) noexcept
227{
228 if (haystack.isNull())
229 return needle.isNull();
230 const auto haystackLen = haystack.size();
231 const auto needleLen = needle.size();
232 if (haystackLen == 0)
233 return needleLen == 0;
234 if (needleLen > haystackLen)
235 return false;
236
237 return QtPrivate::compareStrings(haystack.first(needleLen), needle, cs) == 0;
238}
239
240template <typename Haystack, typename Needle>
241bool qt_ends_with_impl(Haystack haystack, Needle needle, Qt::CaseSensitivity cs) noexcept
243 if (haystack.isNull())
244 return needle.isNull();
245 const auto haystackLen = haystack.size();
246 const auto needleLen = needle.size();
247 if (haystackLen == 0)
248 return needleLen == 0;
249 if (haystackLen < needleLen)
250 return false;
251
252 return QtPrivate::compareStrings(haystack.last(needleLen), needle, cs) == 0;
253}
254
255template <typename T>
256static void append_helper(QString &self, T view)
257{
258 const auto strData = view.data();
259 const qsizetype strSize = view.size();
260 auto &d = self.data_ptr();
261 if (strData && strSize > 0) {
262 // the number of UTF-8 code units is always at a minimum equal to the number
263 // of equivalent UTF-16 code units
264 d.detachAndGrow(QArrayData::GrowsAtEnd, strSize, nullptr, nullptr);
265 Q_CHECK_PTR(d.data());
266 Q_ASSERT(strSize <= d.freeSpaceAtEnd());
267
268 auto dst = std::next(d.data(), d.size);
269 if constexpr (std::is_same_v<T, QUtf8StringView>) {
271 } else if constexpr (std::is_same_v<T, QLatin1StringView>) {
273 dst += strSize;
274 } else {
276 "Can only operate on UTF-8 and Latin-1");
277 }
278 self.resize(std::distance(d.begin(), dst));
279 } else if (d.isNull() && !view.isNull()) { // special case
281 }
282}
283
284template <uint MaxCount> struct UnrollTailLoop
285{
286 template <typename RetType, typename Functor1, typename Functor2, typename Number>
287 static inline RetType exec(Number count, RetType returnIfExited, Functor1 loopCheck, Functor2 returnIfFailed, Number i = 0)
288 {
289 /* equivalent to:
290 * while (count--) {
291 * if (loopCheck(i))
292 * return returnIfFailed(i);
293 * }
294 * return returnIfExited;
295 */
296
297 if (!count)
298 return returnIfExited;
299
300 bool check = loopCheck(i);
301 if (check)
302 return returnIfFailed(i);
303
304 return UnrollTailLoop<MaxCount - 1>::exec(count - 1, returnIfExited, loopCheck, returnIfFailed, i + 1);
305 }
306
307 template <typename Functor, typename Number>
308 static inline void exec(Number count, Functor code)
309 {
310 /* equivalent to:
311 * for (Number i = 0; i < count; ++i)
312 * code(i);
313 */
314 exec(count, 0, [=](Number i) -> bool { code(i); return false; }, [](Number) { return 0; });
315 }
316};
317template <> template <typename RetType, typename Functor1, typename Functor2, typename Number>
318inline RetType UnrollTailLoop<0>::exec(Number, RetType returnIfExited, Functor1, Functor2, Number)
319{
320 return returnIfExited;
321}
322} // unnamed namespace
323
324/*
325 * Note on the use of SIMD in qstring.cpp:
326 *
327 * Several operations with strings are improved with the use of SIMD code,
328 * since they are repetitive. For MIPS, we have hand-written assembly code
329 * outside of qstring.cpp targeting MIPS DSP and MIPS DSPr2. For ARM and for
330 * x86, we can only use intrinsics and therefore everything is contained in
331 * qstring.cpp. We need to use intrinsics only for those platforms due to the
332 * different compilers and toolchains used, which have different syntax for
333 * assembly sources.
334 *
335 * ** SSE notes: **
336 *
337 * Whenever multiple alternatives are equivalent or near so, we prefer the one
338 * using instructions from SSE2, since SSE2 is guaranteed to be enabled for all
339 * 64-bit builds and we enable it for 32-bit builds by default. Use of higher
340 * SSE versions should be done when there is a clear performance benefit and
341 * requires fallback code to SSE2, if it exists.
342 *
343 * Performance measurement in the past shows that most strings are short in
344 * size and, therefore, do not benefit from alignment prologues. That is,
345 * trying to find a 16-byte-aligned boundary to operate on is often more
346 * expensive than executing the unaligned operation directly. In addition, note
347 * that the QString private data is designed so that the data is stored on
348 * 16-byte boundaries if the system malloc() returns 16-byte aligned pointers
349 * on its own (64-bit glibc on Linux does; 32-bit glibc on Linux returns them
350 * 50% of the time), so skipping the alignment prologue is actually optimizing
351 * for the common case.
352 */
353
354#if defined(__mips_dsp)
355// From qstring_mips_dsp_asm.S
356extern "C" void qt_fromlatin1_mips_asm_unroll4 (char16_t*, const char*, uint);
357extern "C" void qt_fromlatin1_mips_asm_unroll8 (char16_t*, const char*, uint);
358extern "C" void qt_toLatin1_mips_dsp_asm(uchar *dst, const char16_t *src, int length);
359#endif
360
361#if defined(__SSE2__) && defined(Q_CC_GNU)
362// We may overrun the buffer, but that's a false positive:
363// this won't crash nor produce incorrect results
364# define ATTRIBUTE_NO_SANITIZE __attribute__((__no_sanitize_address__))
365#else
366# define ATTRIBUTE_NO_SANITIZE
367#endif
368
369#ifdef __SSE2__
370static constexpr bool UseSse4_1 = bool(qCompilerCpuFeatures & CpuFeatureSSE4_1);
371static constexpr bool UseAvx2 = UseSse4_1 &&
372 (qCompilerCpuFeatures & CpuFeatureArchHaswell) == CpuFeatureArchHaswell;
373
374[[maybe_unused]]
375static Q_ALWAYS_INLINE __m128i mm_load8_zero_extend(const void *ptr)
376{
377 const __m128i *dataptr = static_cast<const __m128i *>(ptr);
378 if constexpr (UseSse4_1) {
379 // use a MOVQ followed by PMOVZXBW
380 // if AVX2 is present, these should combine into a single VPMOVZXBW instruction
381 __m128i data = _mm_loadl_epi64(dataptr);
382 return _mm_cvtepu8_epi16(data);
383 }
384
385 // use MOVQ followed by PUNPCKLBW
386 __m128i data = _mm_loadl_epi64(dataptr);
387 return _mm_unpacklo_epi8(data, _mm_setzero_si128());
388}
389
390[[maybe_unused]] ATTRIBUTE_NO_SANITIZE
391static qsizetype qustrlen_sse2(const char16_t *str) noexcept
392{
393 // find the 16-byte alignment immediately prior or equal to str
394 quintptr misalignment = quintptr(str) & 0xf;
395 Q_ASSERT((misalignment & 1) == 0);
396 const char16_t *ptr = str - (misalignment / 2);
397
398 // load 16 bytes and see if we have a null
399 // (aligned loads can never segfault)
400 const __m128i zeroes = _mm_setzero_si128();
401 __m128i data = _mm_load_si128(reinterpret_cast<const __m128i *>(ptr));
402 __m128i comparison = _mm_cmpeq_epi16(data, zeroes);
403 uint mask = _mm_movemask_epi8(comparison);
404
405 // ignore the result prior to the beginning of str
406 mask >>= misalignment;
407
408 // Have we found something in the first block? Need to handle it now
409 // because of the left shift above.
410 if (mask)
411 return qCountTrailingZeroBits(mask) / sizeof(char16_t);
412
413 constexpr qsizetype Step = sizeof(__m128i) / sizeof(char16_t);
414 qsizetype size = Step - misalignment / sizeof(char16_t);
415
416 size -= Step;
417 do {
418 size += Step;
419 data = _mm_load_si128(reinterpret_cast<const __m128i *>(str + size));
420
421 comparison = _mm_cmpeq_epi16(data, zeroes);
422 mask = _mm_movemask_epi8(comparison);
423 } while (mask == 0);
424
425 // found a null
426 return size + qCountTrailingZeroBits(mask) / sizeof(char16_t);
427}
428
429// Scans from \a ptr to \a end until \a maskval is non-zero. Returns true if
430// the no non-zero was found. Returns false and updates \a ptr to point to the
431// first 16-bit word that has any bit set (note: if the input is 8-bit, \a ptr
432// may be updated to one byte short).
433static bool simdTestMask(const char *&ptr, const char *end, quint32 maskval)
434{
435 auto updatePtr = [&](uint result) {
436 // found a character matching the mask
438 ptr += idx;
439 return false;
440 };
441
442 if constexpr (UseSse4_1) {
443# ifndef Q_OS_QNX // compiler fails in the code below
444 __m128i mask;
445 auto updatePtrSimd = [&](__m128i data) -> bool {
446 __m128i masked = _mm_and_si128(mask, data);
447 __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128());
448 uint result = _mm_movemask_epi8(comparison);
449 return updatePtr(result);
450 };
451
452 if constexpr (UseAvx2) {
453 // AVX2 implementation: test 32 bytes at a time
454 const __m256i mask256 = _mm256_broadcastd_epi32(_mm_cvtsi32_si128(maskval));
455 while (ptr + 32 <= end) {
456 __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr));
457 if (!_mm256_testz_si256(mask256, data)) {
458 // found a character matching the mask
459 __m256i masked256 = _mm256_and_si256(mask256, data);
460 __m256i comparison256 = _mm256_cmpeq_epi16(masked256, _mm256_setzero_si256());
461 return updatePtr(_mm256_movemask_epi8(comparison256));
462 }
463 ptr += 32;
464 }
465
466 mask = _mm256_castsi256_si128(mask256);
467 } else {
468 // SSE 4.1 implementation: test 32 bytes at a time (two 16-byte
469 // comparisons, unrolled)
470 mask = _mm_set1_epi32(maskval);
471 while (ptr + 32 <= end) {
472 __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
473 __m128i data2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr + 16));
474 if (!_mm_testz_si128(mask, data1))
475 return updatePtrSimd(data1);
476
477 ptr += 16;
478 if (!_mm_testz_si128(mask, data2))
479 return updatePtrSimd(data2);
480 ptr += 16;
481 }
482 }
483
484 // AVX2 and SSE4.1: final 16-byte comparison
485 if (ptr + 16 <= end) {
486 __m128i data1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
487 if (!_mm_testz_si128(mask, data1))
488 return updatePtrSimd(data1);
489 ptr += 16;
490 }
491
492 // and final 8-byte comparison
493 if (ptr + 8 <= end) {
494 __m128i data1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr));
495 if (!_mm_testz_si128(mask, data1))
496 return updatePtrSimd(data1);
497 ptr += 8;
498 }
499
500 return true;
501# endif // QNX
502 }
503
504 // SSE2 implementation: test 16 bytes at a time.
505 const __m128i mask = _mm_set1_epi32(maskval);
506 while (ptr + 16 <= end) {
507 __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
508 __m128i masked = _mm_and_si128(mask, data);
509 __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128());
510 quint16 result = _mm_movemask_epi8(comparison);
511 if (result != 0xffff)
512 return updatePtr(result);
513 ptr += 16;
514 }
515
516 // and one 8-byte comparison
517 if (ptr + 8 <= end) {
518 __m128i data = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr));
519 __m128i masked = _mm_and_si128(mask, data);
520 __m128i comparison = _mm_cmpeq_epi16(masked, _mm_setzero_si128());
521 quint8 result = _mm_movemask_epi8(comparison);
522 if (result != 0xff)
523 return updatePtr(result);
524 ptr += 8;
525 }
526
527 return true;
528}
529
530template <StringComparisonMode Mode, typename Char> [[maybe_unused]]
531static int ucstrncmp_sse2(const char16_t *a, const Char *b, size_t l)
532{
533 static_assert(std::is_unsigned_v<Char>);
534
535 // Using the PMOVMSKB instruction, we get two bits for each UTF-16 character
536 // we compare. This lambda helps extract the code unit.
537 static const auto codeUnitAt = [](const auto *n, qptrdiff idx) -> int {
538 constexpr int Stride = 2;
539 // this is the same as:
540 // return n[idx / Stride];
541 // but using pointer arithmetic to avoid the compiler dividing by two
542 // and multiplying by two in the case of char16_t (we know idx is even,
543 // but the compiler does not). This is not UB.
544
545 auto ptr = reinterpret_cast<const uchar *>(n);
546 ptr += idx / (Stride / sizeof(*n));
547 return *reinterpret_cast<decltype(n)>(ptr);
548 };
549 auto difference = [a, b](uint mask, qptrdiff offset) {
550 if (Mode == CompareStringsForEquality)
551 return 1;
553 return codeUnitAt(a + offset, idx) - codeUnitAt(b + offset, idx);
554 };
555
556 static const auto load8Chars = [](const auto *ptr) {
557 if (sizeof(*ptr) == 2)
558 return _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
559 __m128i chunk = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr));
560 return _mm_unpacklo_epi8(chunk, _mm_setzero_si128());
561 };
562 static const auto load4Chars = [](const auto *ptr) {
563 if (sizeof(*ptr) == 2)
564 return _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr));
565 __m128i chunk = _mm_cvtsi32_si128(qFromUnaligned<quint32>(ptr));
566 return _mm_unpacklo_epi8(chunk, _mm_setzero_si128());
567 };
568
569 // we're going to read a[0..15] and b[0..15] (32 bytes)
570 auto processChunk16Chars = [a, b](qptrdiff offset) -> uint {
571 if constexpr (UseAvx2) {
572 __m256i a_data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(a + offset));
573 __m256i b_data;
574 if (sizeof(Char) == 1) {
575 // expand to UTF-16 via zero-extension
576 __m128i chunk = _mm_loadu_si128(reinterpret_cast<const __m128i *>(b + offset));
577 b_data = _mm256_cvtepu8_epi16(chunk);
578 } else {
579 b_data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(b + offset));
580 }
581 __m256i result = _mm256_cmpeq_epi16(a_data, b_data);
582 return _mm256_movemask_epi8(result);
583 }
584
585 __m128i a_data1 = load8Chars(a + offset);
586 __m128i a_data2 = load8Chars(a + offset + 8);
587 __m128i b_data1, b_data2;
588 if (sizeof(Char) == 1) {
589 // expand to UTF-16 via unpacking
590 __m128i b_data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(b + offset));
591 b_data1 = _mm_unpacklo_epi8(b_data, _mm_setzero_si128());
592 b_data2 = _mm_unpackhi_epi8(b_data, _mm_setzero_si128());
593 } else {
594 b_data1 = load8Chars(b + offset);
595 b_data2 = load8Chars(b + offset + 8);
596 }
597 __m128i result1 = _mm_cmpeq_epi16(a_data1, b_data1);
598 __m128i result2 = _mm_cmpeq_epi16(a_data2, b_data2);
599 return _mm_movemask_epi8(result1) | _mm_movemask_epi8(result2) << 16;
600 };
601
602 if (l >= sizeof(__m256i) / sizeof(char16_t)) {
603 qptrdiff offset = 0;
604 for ( ; l >= offset + sizeof(__m256i) / sizeof(char16_t); offset += sizeof(__m256i) / sizeof(char16_t)) {
605 uint mask = ~processChunk16Chars(offset);
606 if (mask)
607 return difference(mask, offset);
608 }
609
610 // maybe overlap the last 32 bytes
611 if (size_t(offset) < l) {
612 offset = l - sizeof(__m256i) / sizeof(char16_t);
613 uint mask = ~processChunk16Chars(offset);
614 return mask ? difference(mask, offset) : 0;
615 }
616 } else if (l >= 4) {
617 __m128i a_data1, b_data1;
618 __m128i a_data2, b_data2;
619 int width;
620 if (l >= 8) {
621 width = 8;
622 a_data1 = load8Chars(a);
623 b_data1 = load8Chars(b);
624 a_data2 = load8Chars(a + l - width);
625 b_data2 = load8Chars(b + l - width);
626 } else {
627 // we're going to read a[0..3] and b[0..3] (8 bytes)
628 width = 4;
629 a_data1 = load4Chars(a);
630 b_data1 = load4Chars(b);
631 a_data2 = load4Chars(a + l - width);
632 b_data2 = load4Chars(b + l - width);
633 }
634
635 __m128i result = _mm_cmpeq_epi16(a_data1, b_data1);
636 ushort mask = ~_mm_movemask_epi8(result);
637 if (mask)
638 return difference(mask, 0);
639
640 result = _mm_cmpeq_epi16(a_data2, b_data2);
641 mask = ~_mm_movemask_epi8(result);
642 if (mask)
643 return difference(mask, l - width);
644 } else {
645 // reset l
646 l &= 3;
647
648 const auto lambda = [=](size_t i) -> int {
649 return a[i] - b[i];
650 };
651 return UnrollTailLoop<3>::exec(l, 0, lambda, lambda);
652 }
653 return 0;
654}
655#endif
656
658qsizetype QtPrivate::qustrlen(const char16_t *str) noexcept
659{
660#if defined(__SSE2__) && !(defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer))
661 return qustrlen_sse2(str);
662#endif
663
664 if (sizeof(wchar_t) == sizeof(char16_t))
665 return wcslen(reinterpret_cast<const wchar_t *>(str));
666
667 qsizetype result = 0;
668 while (*str++)
669 ++result;
670 return result;
671}
672
673qsizetype QtPrivate::qustrnlen(const char16_t *str, qsizetype maxlen) noexcept
674{
675 return qustrchr({ str, maxlen }, u'\0') - str;
676}
677
687const char16_t *QtPrivate::qustrchr(QStringView str, char16_t c) noexcept
688{
689 const char16_t *n = str.utf16();
690 const char16_t *e = n + str.size();
691
692#ifdef __SSE2__
693 bool loops = true;
694 // Using the PMOVMSKB instruction, we get two bits for each character
695 // we compare.
696 __m128i mch;
697 if constexpr (UseAvx2) {
698 // we're going to read n[0..15] (32 bytes)
699 __m256i mch256 = _mm256_set1_epi32(c | (c << 16));
700 for (const char16_t *next = n + 16; next <= e; n = next, next += 16) {
701 __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(n));
702 __m256i result = _mm256_cmpeq_epi16(data, mch256);
703 uint mask = uint(_mm256_movemask_epi8(result));
704 if (mask) {
706 return n + idx / 2;
707 }
708 }
709 loops = false;
710 mch = _mm256_castsi256_si128(mch256);
711 } else {
712 mch = _mm_set1_epi32(c | (c << 16));
713 }
714
715 auto hasMatch = [mch, &n](__m128i data, ushort validityMask) {
716 __m128i result = _mm_cmpeq_epi16(data, mch);
717 uint mask = uint(_mm_movemask_epi8(result));
718 if ((mask & validityMask) == 0)
719 return false;
721 n += idx / 2;
722 return true;
723 };
724
725 // we're going to read n[0..7] (16 bytes)
726 for (const char16_t *next = n + 8; next <= e; n = next, next += 8) {
727 __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(n));
728 if (hasMatch(data, 0xffff))
729 return n;
730
731 if (!loops) {
732 n += 8;
733 break;
734 }
735 }
736
737# if !defined(__OPTIMIZE_SIZE__)
738 // we're going to read n[0..3] (8 bytes)
739 if (e - n > 3) {
740 __m128i data = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(n));
741 if (hasMatch(data, 0xff))
742 return n;
743
744 n += 4;
745 }
746
747 return UnrollTailLoop<3>::exec(e - n, e,
748 [=](qsizetype i) { return n[i] == c; },
749 [=](qsizetype i) { return n + i; });
750# endif
751#elif defined(__ARM_NEON__)
752 const uint16x8_t vmask = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 };
753 const uint16x8_t ch_vec = vdupq_n_u16(c);
754 for (const char16_t *next = n + 8; next <= e; n = next, next += 8) {
755 uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t *>(n));
756 uint mask = vaddvq_u16(vandq_u16(vceqq_u16(data, ch_vec), vmask));
757 if (ushort(mask)) {
758 // found a match
759 return n + qCountTrailingZeroBits(mask);
760 }
761 }
762#endif // aarch64
763
764 return std::find(n, e, c);
765}
766
775const char16_t *QtPrivate::qustrcasechr(QStringView str, char16_t c) noexcept
776{
777 const QChar *n = str.begin();
778 const QChar *e = str.end();
779 c = foldCase(c);
780 auto it = std::find_if(n, e, [c](auto ch) { return foldAndCompare(ch, QChar(c)); });
781 return reinterpret_cast<const char16_t *>(it);
782}
783
784// Note: ptr on output may be off by one and point to a preceding US-ASCII
785// character. Usually harmless.
786bool qt_is_ascii(const char *&ptr, const char *end) noexcept
787{
788#if defined(__SSE2__)
789 // Testing for the high bit can be done efficiently with just PMOVMSKB
790 bool loops = true;
791 if constexpr (UseAvx2) {
792 while (ptr + 32 <= end) {
793 __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr));
794 quint32 mask = _mm256_movemask_epi8(data);
795 if (mask) {
797 ptr += idx;
798 return false;
799 }
800 ptr += 32;
801 }
802 loops = false;
803 }
804
805 while (ptr + 16 <= end) {
806 __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
807 quint32 mask = _mm_movemask_epi8(data);
808 if (mask) {
810 ptr += idx;
811 return false;
812 }
813 ptr += 16;
814
815 if (!loops)
816 break;
817 }
818 if (ptr + 8 <= end) {
819 __m128i data = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(ptr));
820 quint8 mask = _mm_movemask_epi8(data);
821 if (mask) {
823 ptr += idx;
824 return false;
825 }
826 ptr += 8;
827 }
828#endif
829
830 while (ptr + 4 <= end) {
831 quint32 data = qFromUnaligned<quint32>(ptr);
832 if (data &= 0x80808080U) {
836 ptr += idx / 8;
837 return false;
838 }
839 ptr += 4;
840 }
841
842 while (ptr != end) {
843 if (quint8(*ptr) & 0x80)
844 return false;
845 ++ptr;
846 }
847 return true;
848}
849
851{
852 const char *ptr = s.begin();
853 const char *end = s.end();
854
855 return qt_is_ascii(ptr, end);
856}
857
858static bool isAscii_helper(const char16_t *&ptr, const char16_t *end)
859{
860#ifdef __SSE2__
861 const char *ptr8 = reinterpret_cast<const char *>(ptr);
862 const char *end8 = reinterpret_cast<const char *>(end);
863 bool ok = simdTestMask(ptr8, end8, 0xff80ff80);
864 ptr = reinterpret_cast<const char16_t *>(ptr8);
865 if (!ok)
866 return false;
867#endif
868
869 while (ptr != end) {
870 if (*ptr & 0xff80)
871 return false;
872 ++ptr;
873 }
874 return true;
875}
876
878{
879 const char16_t *ptr = s.utf16();
880 const char16_t *end = ptr + s.size();
881
882 return isAscii_helper(ptr, end);
883}
884
886{
887 const char16_t *ptr = s.utf16();
888 const char16_t *end = ptr + s.size();
889
890#ifdef __SSE2__
891 const char *ptr8 = reinterpret_cast<const char *>(ptr);
892 const char *end8 = reinterpret_cast<const char *>(end);
893 if (!simdTestMask(ptr8, end8, 0xff00ff00))
894 return false;
895 ptr = reinterpret_cast<const char16_t *>(ptr8);
896#endif
897
898 while (ptr != end) {
899 if (*ptr++ > 0xff)
900 return false;
901 }
902 return true;
903}
904
906{
907 constexpr char32_t InvalidCodePoint = UINT_MAX;
908
910 while (i.hasNext()) {
911 const char32_t c = i.next(InvalidCodePoint);
912 if (c == InvalidCodePoint)
913 return false;
914 }
915
916 return true;
917}
918
919// conversion between Latin 1 and UTF-16
920Q_CORE_EXPORT void qt_from_latin1(char16_t *dst, const char *str, size_t size) noexcept
921{
922 /* SIMD:
923 * Unpacking with SSE has been shown to improve performance on recent CPUs
924 * The same method gives no improvement with NEON. On Aarch64, clang will do the vectorization
925 * itself in exactly the same way as one would do it with intrinsics.
926 */
927#if defined(__SSE2__)
928 // we're going to read str[offset..offset+15] (16 bytes)
929 const __m128i nullMask = _mm_setzero_si128();
930 auto processOneChunk = [=](qptrdiff offset) {
931 const __m128i chunk = _mm_loadu_si128((const __m128i*)(str + offset)); // load
932 if constexpr (UseAvx2) {
933 // zero extend to an YMM register
934 const __m256i extended = _mm256_cvtepu8_epi16(chunk);
935
936 // store
937 _mm256_storeu_si256((__m256i*)(dst + offset), extended);
938 } else {
939 // unpack the first 8 bytes, padding with zeros
940 const __m128i firstHalf = _mm_unpacklo_epi8(chunk, nullMask);
941 _mm_storeu_si128((__m128i*)(dst + offset), firstHalf); // store
942
943 // unpack the last 8 bytes, padding with zeros
944 const __m128i secondHalf = _mm_unpackhi_epi8 (chunk, nullMask);
945 _mm_storeu_si128((__m128i*)(dst + offset + 8), secondHalf); // store
946 }
947 };
948
949 const char *e = str + size;
950 if (size >= sizeof(__m128i)) {
951 qptrdiff offset = 0;
952 for ( ; str + offset + sizeof(__m128i) <= e; offset += sizeof(__m128i))
953 processOneChunk(offset);
954 if (str + offset < e)
955 processOneChunk(size - sizeof(__m128i));
956 return;
957 }
958
959# if !defined(__OPTIMIZE_SIZE__)
960 if (size >= 4) {
961 // two overlapped loads & stores, of either 64-bit or of 32-bit
962 if (size >= 8) {
963 const __m128i unpacked1 = mm_load8_zero_extend(str);
964 const __m128i unpacked2 = mm_load8_zero_extend(str + size - 8);
965 _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), unpacked1);
966 _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + size - 8), unpacked2);
967 } else {
968 const __m128i chunk1 = _mm_cvtsi32_si128(qFromUnaligned<quint32>(str));
969 const __m128i chunk2 = _mm_cvtsi32_si128(qFromUnaligned<quint32>(str + size - 4));
970 const __m128i unpacked1 = _mm_unpacklo_epi8(chunk1, nullMask);
971 const __m128i unpacked2 = _mm_unpacklo_epi8(chunk2, nullMask);
972 _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), unpacked1);
973 _mm_storel_epi64(reinterpret_cast<__m128i *>(dst + size - 4), unpacked2);
974 }
975 return;
976 } else {
977 size = size % 4;
978 return UnrollTailLoop<3>::exec(qsizetype(size), [=](qsizetype i) { dst[i] = uchar(str[i]); });
979 }
980# endif
981#endif
982#if defined(__mips_dsp)
983 static_assert(sizeof(qsizetype) == sizeof(int),
984 "oops, the assembler implementation needs to be called in a loop");
985 if (size > 20)
986 qt_fromlatin1_mips_asm_unroll8(dst, str, size);
987 else
988 qt_fromlatin1_mips_asm_unroll4(dst, str, size);
989#else
990 while (size--)
991 *dst++ = (uchar)*str++;
992#endif
993}
994
995static QVarLengthArray<char16_t> qt_from_latin1_to_qvla(QLatin1StringView str)
996{
997 const qsizetype len = str.size();
998 QVarLengthArray<char16_t> arr(len);
999 qt_from_latin1(arr.data(), str.data(), len);
1000 return arr;
1001}
1002
1003template <bool Checked>
1004static void qt_to_latin1_internal(uchar *dst, const char16_t *src, qsizetype length)
1005{
1006#if defined(__SSE2__)
1007 auto questionMark256 = []() {
1008 if constexpr (UseAvx2)
1009 return _mm256_broadcastw_epi16(_mm_cvtsi32_si128('?'));
1010 else
1011 return 0;
1012 }();
1013 auto outOfRange256 = []() {
1014 if constexpr (UseAvx2)
1015 return _mm256_broadcastw_epi16(_mm_cvtsi32_si128(0x100));
1016 else
1017 return 0;
1018 }();
1019 __m128i questionMark, outOfRange;
1020 if constexpr (UseAvx2) {
1021 questionMark = _mm256_castsi256_si128(questionMark256);
1022 outOfRange = _mm256_castsi256_si128(outOfRange256);
1023 } else {
1024 questionMark = _mm_set1_epi16('?');
1025 outOfRange = _mm_set1_epi16(0x100);
1026 }
1027
1028 auto mergeQuestionMarks = [=](__m128i chunk) {
1029 if (!Checked)
1030 return chunk;
1031
1032 // SSE has no compare instruction for unsigned comparison.
1033 if constexpr (UseSse4_1) {
1034 // We use an unsigned uc = qMin(uc, 0x100) and then compare for equality.
1035 chunk = _mm_min_epu16(chunk, outOfRange);
1036 const __m128i offLimitMask = _mm_cmpeq_epi16(chunk, outOfRange);
1037 chunk = _mm_blendv_epi8(chunk, questionMark, offLimitMask);
1038 return chunk;
1039 }
1040 // The variables must be shiffted + 0x8000 to be compared
1041 const __m128i signedBitOffset = _mm_set1_epi16(short(0x8000));
1042 const __m128i thresholdMask = _mm_set1_epi16(short(0xff + 0x8000));
1043
1044 const __m128i signedChunk = _mm_add_epi16(chunk, signedBitOffset);
1045 const __m128i offLimitMask = _mm_cmpgt_epi16(signedChunk, thresholdMask);
1046
1047 // offLimitQuestionMark contains '?' for each 16 bits that was off-limit
1048 // the 16 bits that were correct contains zeros
1049 const __m128i offLimitQuestionMark = _mm_and_si128(offLimitMask, questionMark);
1050
1051 // correctBytes contains the bytes that were in limit
1052 // the 16 bits that were off limits contains zeros
1053 const __m128i correctBytes = _mm_andnot_si128(offLimitMask, chunk);
1054
1055 // merge offLimitQuestionMark and correctBytes to have the result
1056 chunk = _mm_or_si128(correctBytes, offLimitQuestionMark);
1057
1058 Q_UNUSED(outOfRange);
1059 return chunk;
1060 };
1061
1062 // we're going to read to src[offset..offset+15] (16 bytes)
1063 auto loadChunkAt = [=](qptrdiff offset) {
1064 __m128i chunk1, chunk2;
1065 if constexpr (UseAvx2) {
1066 __m256i chunk = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + offset));
1067 if (Checked) {
1068 // See mergeQuestionMarks lambda above for details
1069 chunk = _mm256_min_epu16(chunk, outOfRange256);
1070 const __m256i offLimitMask = _mm256_cmpeq_epi16(chunk, outOfRange256);
1071 chunk = _mm256_blendv_epi8(chunk, questionMark256, offLimitMask);
1072 }
1073
1074 chunk2 = _mm256_extracti128_si256(chunk, 1);
1075 chunk1 = _mm256_castsi256_si128(chunk);
1076 } else {
1077 chunk1 = _mm_loadu_si128((const __m128i*)(src + offset)); // load
1078 chunk1 = mergeQuestionMarks(chunk1);
1079
1080 chunk2 = _mm_loadu_si128((const __m128i*)(src + offset + 8)); // load
1081 chunk2 = mergeQuestionMarks(chunk2);
1082 }
1083
1084 // pack the two vector to 16 x 8bits elements
1085 return _mm_packus_epi16(chunk1, chunk2);
1086 };
1087
1088 if (size_t(length) >= sizeof(__m128i)) {
1089 // because of possible overlapping, we won't process the last chunk in the loop
1090 qptrdiff offset = 0;
1091 for ( ; offset + 2 * sizeof(__m128i) < size_t(length); offset += sizeof(__m128i))
1092 _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + offset), loadChunkAt(offset));
1093
1094 // overlapped conversion of the last full chunk and the tail
1095 __m128i last1 = loadChunkAt(offset);
1096 __m128i last2 = loadChunkAt(length - sizeof(__m128i));
1097 _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + offset), last1);
1098 _mm_storeu_si128(reinterpret_cast<__m128i *>(dst + length - sizeof(__m128i)), last2);
1099 return;
1100 }
1101
1102# if !defined(__OPTIMIZE_SIZE__)
1103 if (length >= 4) {
1104 // this code is fine even for in-place conversion because we load both
1105 // before any store
1106 if (length >= 8) {
1107 __m128i chunk1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
1108 __m128i chunk2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + length - 8));
1109 chunk1 = mergeQuestionMarks(chunk1);
1110 chunk2 = mergeQuestionMarks(chunk2);
1111
1112 // pack, where the upper half is ignored
1113 const __m128i result1 = _mm_packus_epi16(chunk1, chunk1);
1114 const __m128i result2 = _mm_packus_epi16(chunk2, chunk2);
1115 _mm_storel_epi64(reinterpret_cast<__m128i *>(dst), result1);
1116 _mm_storel_epi64(reinterpret_cast<__m128i *>(dst + length - 8), result2);
1117 } else {
1118 __m128i chunk1 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(src));
1119 __m128i chunk2 = _mm_loadl_epi64(reinterpret_cast<const __m128i *>(src + length - 4));
1120 chunk1 = mergeQuestionMarks(chunk1);
1121 chunk2 = mergeQuestionMarks(chunk2);
1122
1123 // pack, we'll zero the upper three quarters
1124 const __m128i result1 = _mm_packus_epi16(chunk1, chunk1);
1125 const __m128i result2 = _mm_packus_epi16(chunk2, chunk2);
1126 qToUnaligned(_mm_cvtsi128_si32(result1), dst);
1127 qToUnaligned(_mm_cvtsi128_si32(result2), dst + length - 4);
1128 }
1129 return;
1130 }
1131
1132 length = length % 4;
1133 return UnrollTailLoop<3>::exec(length, [=](qsizetype i) {
1134 if (Checked)
1135 dst[i] = (src[i]>0xff) ? '?' : (uchar) src[i];
1136 else
1137 dst[i] = src[i];
1138 });
1139# else
1140 length = length % 16;
1141# endif // optimize size
1142#elif defined(__ARM_NEON__)
1143 // Refer to the documentation of the SSE2 implementation.
1144 // This uses exactly the same method as for SSE except:
1145 // 1) neon has unsigned comparison
1146 // 2) packing is done to 64 bits (8 x 8bits component).
1147 if (length >= 16) {
1148 const qsizetype chunkCount = length >> 3; // divided by 8
1149 const uint16x8_t questionMark = vdupq_n_u16('?'); // set
1150 const uint16x8_t thresholdMask = vdupq_n_u16(0xff); // set
1151 for (qsizetype i = 0; i < chunkCount; ++i) {
1152 uint16x8_t chunk = vld1q_u16((uint16_t *)src); // load
1153 src += 8;
1154
1155 if (Checked) {
1156 const uint16x8_t offLimitMask = vcgtq_u16(chunk, thresholdMask); // chunk > thresholdMask
1157 const uint16x8_t offLimitQuestionMark = vandq_u16(offLimitMask, questionMark); // offLimitMask & questionMark
1158 const uint16x8_t correctBytes = vbicq_u16(chunk, offLimitMask); // !offLimitMask & chunk
1159 chunk = vorrq_u16(correctBytes, offLimitQuestionMark); // correctBytes | offLimitQuestionMark
1160 }
1161 const uint8x8_t result = vmovn_u16(chunk); // narrowing move->packing
1162 vst1_u8(dst, result); // store
1163 dst += 8;
1164 }
1165 length = length % 8;
1166 }
1167#endif
1168#if defined(__mips_dsp)
1169 static_assert(sizeof(qsizetype) == sizeof(int),
1170 "oops, the assembler implementation needs to be called in a loop");
1171 qt_toLatin1_mips_dsp_asm(dst, src, length);
1172#else
1173 while (length--) {
1174 if (Checked)
1175 *dst++ = (*src>0xff) ? '?' : (uchar) *src;
1176 else
1177 *dst++ = *src;
1178 ++src;
1179 }
1180#endif
1181}
1182
1183void qt_to_latin1(uchar *dst, const char16_t *src, qsizetype length)
1184{
1185 qt_to_latin1_internal<true>(dst, src, length);
1186}
1187
1189{
1190 qt_to_latin1_internal<false>(dst, src, length);
1191}
1192
1193// Unicode case-insensitive comparison (argument order matches QStringView)
1194Q_NEVER_INLINE static int ucstricmp(qsizetype alen, const char16_t *a, qsizetype blen, const char16_t *b)
1195{
1196 if (a == b)
1197 return qt_lencmp(alen, blen);
1198
1199 char32_t alast = 0;
1200 char32_t blast = 0;
1201 qsizetype l = qMin(alen, blen);
1202 qsizetype i;
1203 for (i = 0; i < l; ++i) {
1204// qDebug() << Qt::hex << alast << blast;
1205// qDebug() << Qt::hex << "*a=" << *a << "alast=" << alast << "folded=" << foldCase (*a, alast);
1206// qDebug() << Qt::hex << "*b=" << *b << "blast=" << blast << "folded=" << foldCase (*b, blast);
1207 int diff = foldCase(a[i], alast) - foldCase(b[i], blast);
1208 if ((diff))
1209 return diff;
1210 }
1211 if (i == alen) {
1212 if (i == blen)
1213 return 0;
1214 return -1;
1215 }
1216 return 1;
1217}
1218
1219// Case-insensitive comparison between a QStringView and a QLatin1StringView
1220// (argument order matches those types)
1221Q_NEVER_INLINE static int ucstricmp(qsizetype alen, const char16_t *a, qsizetype blen, const char *b)
1222{
1223 qsizetype l = qMin(alen, blen);
1224 qsizetype i;
1225 for (i = 0; i < l; ++i) {
1226 int diff = foldCase(a[i]) - foldCase(char16_t{uchar(b[i])});
1227 if ((diff))
1228 return diff;
1229 }
1230 if (i == alen) {
1231 if (i == blen)
1232 return 0;
1233 return -1;
1234 }
1235 return 1;
1236}
1237
1238// Case-insensitive comparison between a Unicode string and a UTF-8 string
1239Q_NEVER_INLINE static int ucstricmp8(const char *utf8, const char *utf8end, const QChar *utf16, const QChar *utf16end)
1240{
1241 auto src1 = reinterpret_cast<const uchar *>(utf8);
1242 auto end1 = reinterpret_cast<const uchar *>(utf8end);
1243 QStringIterator src2(utf16, utf16end);
1244
1245 while (src1 < end1 && src2.hasNext()) {
1246 char32_t uc1 = 0;
1247 char32_t *output = &uc1;
1248 uchar b = *src1++;
1249 const qsizetype res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, output, src1, end1);
1250 if (res < 0) {
1251 // decoding error
1252 uc1 = QChar::ReplacementCharacter;
1253 } else {
1254 uc1 = QChar::toCaseFolded(uc1);
1255 }
1256
1257 char32_t uc2 = QChar::toCaseFolded(src2.next());
1258 int diff = uc1 - uc2; // can't underflow
1259 if (diff)
1260 return diff;
1261 }
1262
1263 // the shorter string sorts first
1264 return (end1 > src1) - int(src2.hasNext());
1265}
1266
1267#if defined(__mips_dsp)
1268// From qstring_mips_dsp_asm.S
1269extern "C" int qt_ucstrncmp_mips_dsp_asm(const char16_t *a,
1270 const char16_t *b,
1271 unsigned len);
1272#endif
1273
1274// Unicode case-sensitive compare two same-sized strings
1275template <StringComparisonMode Mode>
1276static int ucstrncmp(const char16_t *a, const char16_t *b, size_t l)
1277{
1278 // This function isn't memcmp() because that can return the wrong sorting
1279 // result in little-endian architectures: 0x00ff must sort before 0x0100,
1280 // but the bytes in memory are FF 00 and 00 01.
1281
1282#ifndef __OPTIMIZE_SIZE__
1283# if defined(__mips_dsp)
1284 static_assert(sizeof(uint) == sizeof(size_t));
1285 if (l >= 8) {
1286 return qt_ucstrncmp_mips_dsp_asm(a, b, l);
1287 }
1288# elif defined(__SSE2__)
1289 return ucstrncmp_sse2<Mode>(a, b, l);
1290# elif defined(__ARM_NEON__)
1291 if (l >= 8) {
1292 const char16_t *end = a + l;
1293 const uint16x8_t mask = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 };
1294 while (end - a > 7) {
1295 uint16x8_t da = vld1q_u16(reinterpret_cast<const uint16_t *>(a));
1296 uint16x8_t db = vld1q_u16(reinterpret_cast<const uint16_t *>(b));
1297
1298 uint8_t r = ~(uint8_t)vaddvq_u16(vandq_u16(vceqq_u16(da, db), mask));
1299 if (r) {
1300 // found a different QChar
1301 if (Mode == CompareStringsForEquality)
1302 return 1;
1304 return a[idx] - b[idx];
1305 }
1306 a += 8;
1307 b += 8;
1308 }
1309 l &= 7;
1310 }
1311 const auto lambda = [=](size_t i) -> int {
1312 return a[i] - b[i];
1313 };
1314 return UnrollTailLoop<7>::exec(l, 0, lambda, lambda);
1315# endif // MIPS DSP or __SSE2__ or __ARM_NEON__
1316#endif // __OPTIMIZE_SIZE__
1317
1318 if (Mode == CompareStringsForEquality || QSysInfo::ByteOrder == QSysInfo::BigEndian)
1319 return memcmp(a, b, l * sizeof(char16_t));
1320
1321 for (size_t i = 0; i < l; ++i) {
1322 if (int diff = a[i] - b[i])
1323 return diff;
1324 }
1325 return 0;
1326}
1327
1328template <StringComparisonMode Mode>
1329static int ucstrncmp(const char16_t *a, const char *b, size_t l)
1330{
1331 const uchar *c = reinterpret_cast<const uchar *>(b);
1332 const char16_t *uc = a;
1333 const char16_t *e = uc + l;
1334
1335#if defined(__SSE2__) && !defined(__OPTIMIZE_SIZE__)
1336 return ucstrncmp_sse2<Mode>(uc, c, l);
1337#endif
1338
1339 while (uc < e) {
1340 int diff = *uc - *c;
1341 if (diff)
1342 return diff;
1343 uc++, c++;
1344 }
1345
1346 return 0;
1347}
1348
1349// Unicode case-sensitive equality
1350template <typename Char2>
1351static bool ucstreq(const char16_t *a, size_t alen, const Char2 *b)
1352{
1353 if constexpr (std::is_same_v<decltype(a), decltype(b)>) {
1354 if (a == b)
1355 return true;
1356 }
1357 return ucstrncmp<CompareStringsForEquality>(a, b, alen) == 0;
1358}
1359
1360// Unicode case-sensitive comparison
1361template <typename Char2>
1362static int ucstrcmp(const char16_t *a, size_t alen, const Char2 *b, size_t blen)
1363{
1364 if constexpr (std::is_same_v<decltype(a), decltype(b)>) {
1365 if (a == b && alen == blen)
1366 return 0;
1367 }
1368 const size_t l = qMin(alen, blen);
1369 int cmp = ucstrncmp<CompareStringsForOrdering>(a, b, l);
1370 return cmp ? cmp : qt_lencmp(alen, blen);
1371}
1372
1374
1375static int latin1nicmp(const char *lhsChar, qsizetype lSize, const char *rhsChar, qsizetype rSize)
1376{
1377 // We're called with QLatin1StringView's .data() and .size():
1378 Q_ASSERT(lSize >= 0 && rSize >= 0);
1379 if (!lSize)
1380 return rSize ? -1 : 0;
1381 if (!rSize)
1382 return 1;
1383 const qsizetype size = std::min(lSize, rSize);
1384
1385 Q_ASSERT(lhsChar && rhsChar); // since both lSize and rSize are positive
1386 for (qsizetype i = 0; i < size; i++) {
1387 if (int res = CaseInsensitiveL1::difference(lhsChar[i], rhsChar[i]))
1388 return res;
1389 }
1390 return qt_lencmp(lSize, rSize);
1391}
1392
1394{
1395 Q_ASSERT(lhs.size() == rhs.size());
1396 return ucstreq(lhs.utf16(), lhs.size(), rhs.utf16());
1397}
1398
1400{
1401 Q_ASSERT(lhs.size() == rhs.size());
1402 return ucstreq(lhs.utf16(), lhs.size(), rhs.latin1());
1403}
1404
1406{
1407 return QtPrivate::equalStrings(rhs, lhs);
1408}
1409
1411{
1412 Q_ASSERT(lhs.size() == rhs.size());
1413 return (!lhs.size() || memcmp(lhs.data(), rhs.data(), lhs.size()) == 0);
1414}
1415
1416bool QtPrivate::equalStrings(QBasicUtf8StringView<false> lhs, QStringView rhs) noexcept
1417{
1418 return QUtf8::compareUtf8(lhs, rhs) == 0;
1419}
1420
1421bool QtPrivate::equalStrings(QStringView lhs, QBasicUtf8StringView<false> rhs) noexcept
1422{
1423 return QtPrivate::equalStrings(rhs, lhs);
1424}
1425
1426bool QtPrivate::equalStrings(QLatin1StringView lhs, QBasicUtf8StringView<false> rhs) noexcept
1427{
1428 return QUtf8::compareUtf8(QByteArrayView(rhs), lhs) == 0;
1429}
1430
1431bool QtPrivate::equalStrings(QBasicUtf8StringView<false> lhs, QLatin1StringView rhs) noexcept
1432{
1433 return QtPrivate::equalStrings(rhs, lhs);
1434}
1435
1436bool QtPrivate::equalStrings(QBasicUtf8StringView<false> lhs, QBasicUtf8StringView<false> rhs) noexcept
1437{
1438#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) || defined(QT_STATIC)
1439 Q_ASSERT(lhs.size() == rhs.size());
1440#else
1441 // operator== didn't enforce size prior to Qt 6.2
1442 if (lhs.size() != rhs.size())
1443 return false;
1444#endif
1445 return (!lhs.size() || memcmp(lhs.data(), rhs.data(), lhs.size()) == 0);
1446}
1447
1449{
1450 if (lhs.size() != rhs.size() && lhs.isUtf8() == rhs.isUtf8())
1451 return false;
1452 return lhs.visit([rhs](auto lhs) {
1453 return rhs.visit([lhs](auto rhs) {
1454 return QtPrivate::equalStrings(lhs, rhs);
1455 });
1456 });
1457}
1458
1475{
1476 if (cs == Qt::CaseSensitive)
1477 return ucstrcmp(lhs.utf16(), lhs.size(), rhs.utf16(), rhs.size());
1478 return ucstricmp(lhs.size(), lhs.utf16(), rhs.size(), rhs.utf16());
1479}
1480
1498{
1499 if (cs == Qt::CaseSensitive)
1500 return ucstrcmp(lhs.utf16(), lhs.size(), rhs.latin1(), rhs.size());
1501 return ucstricmp(lhs.size(), lhs.utf16(), rhs.size(), rhs.latin1());
1502}
1503
1510int QtPrivate::compareStrings(QStringView lhs, QBasicUtf8StringView<false> rhs, Qt::CaseSensitivity cs) noexcept
1511{
1512 return -compareStrings(rhs, lhs, cs);
1513}
1514
1522{
1523 return -compareStrings(rhs, lhs, cs);
1524}
1525
1543{
1544 if (lhs.isEmpty())
1545 return qt_lencmp(qsizetype(0), rhs.size());
1546 if (cs == Qt::CaseInsensitive)
1547 return latin1nicmp(lhs.data(), lhs.size(), rhs.data(), rhs.size());
1548 const auto l = std::min(lhs.size(), rhs.size());
1549 int r = memcmp(lhs.data(), rhs.data(), l);
1550 return r ? r : qt_lencmp(lhs.size(), rhs.size());
1551}
1552
1559int QtPrivate::compareStrings(QLatin1StringView lhs, QBasicUtf8StringView<false> rhs, Qt::CaseSensitivity cs) noexcept
1560{
1561 return -QUtf8::compareUtf8(QByteArrayView(rhs), lhs, cs);
1562}
1563
1570int QtPrivate::compareStrings(QBasicUtf8StringView<false> lhs, QStringView rhs, Qt::CaseSensitivity cs) noexcept
1571{
1572 if (cs == Qt::CaseSensitive)
1573 return QUtf8::compareUtf8(lhs, rhs);
1574 return ucstricmp8(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
1575}
1576
1583int QtPrivate::compareStrings(QBasicUtf8StringView<false> lhs, QLatin1StringView rhs, Qt::CaseSensitivity cs) noexcept
1584{
1585 return -compareStrings(rhs, lhs, cs);
1586}
1587
1594int QtPrivate::compareStrings(QBasicUtf8StringView<false> lhs, QBasicUtf8StringView<false> rhs, Qt::CaseSensitivity cs) noexcept
1595{
1596 return QUtf8::compareUtf8(QByteArrayView(lhs), QByteArrayView(rhs), cs);
1597}
1598
1600{
1601 return lhs.visit([rhs, cs](auto lhs) {
1602 return rhs.visit([lhs, cs](auto rhs) {
1603 return QtPrivate::compareStrings(lhs, rhs, cs);
1604 });
1605 });
1606}
1607
1608// ### Qt 7: do not allow anything but ASCII digits
1609// in arg()'s replacements.
1610#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
1611static bool supportUnicodeDigitValuesInArg()
1612{
1613 static const bool result = []() {
1614 static const char supportUnicodeDigitValuesEnvVar[]
1615 = "QT_USE_UNICODE_DIGIT_VALUES_IN_STRING_ARG";
1616
1617 if (qEnvironmentVariableIsSet(supportUnicodeDigitValuesEnvVar))
1618 return qEnvironmentVariableIntValue(supportUnicodeDigitValuesEnvVar) != 0;
1619
1620#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0) // keep it in sync with the test
1621 return true;
1622#else
1623 return false;
1624#endif
1625 }();
1626
1627 return result;
1628}
1629#endif
1630
1631static int qArgDigitValue(QChar ch) noexcept
1632{
1633#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
1634 if (supportUnicodeDigitValuesInArg())
1635 return ch.digitValue();
1636#endif
1637 if (ch >= u'0' && ch <= u'9')
1638 return int(ch.unicode() - u'0');
1639 return -1;
1640}
1641
1642#if QT_CONFIG(regularexpression)
1644void qtWarnAboutInvalidRegularExpression(const QString &pattern, const char *where);
1645#endif
1646
2213
2216
2383/*
2385Returns a copy of the \a str string. The given string is assumed to be
2386encoded in \1, and is converted to QString using the \2 function.
2388*/
2389
2435qsizetype QString::toUcs4_helper(const char16_t *uc, qsizetype length, char32_t *out)
2436{
2437 qsizetype count = 0;
2438
2440 while (i.hasNext())
2441 out[count++] = i.next();
2442
2443 return count;
2444}
2445
2494{
2495 if (!unicode) {
2496 d.clear();
2497 } else {
2498 if (size < 0)
2499 size = QtPrivate::qustrlen(reinterpret_cast<const char16_t *>(unicode));
2500 if (!size) {
2501 d = DataPointer::fromRawData(&_empty, 0);
2502 } else {
2503 d = DataPointer(size, size);
2504 Q_CHECK_PTR(d.data());
2505 memcpy(d.data(), unicode, size * sizeof(QChar));
2506 d.data()[size] = '\0';
2507 }
2508 }
2509}
2510
2518{
2519 if (size <= 0) {
2520 d = DataPointer::fromRawData(&_empty, 0);
2521 } else {
2522 d = DataPointer(size, size);
2523 Q_CHECK_PTR(d.data());
2524 d.data()[size] = '\0';
2525 char16_t *b = d.data();
2526 char16_t *e = d.data() + size;
2527 const char16_t value = ch.unicode();
2528 std::fill(b, e, value);
2529 }
2530}
2531
2539{
2540 if (size <= 0) {
2541 d = DataPointer::fromRawData(&_empty, 0);
2542 } else {
2543 d = DataPointer(size, size);
2544 Q_CHECK_PTR(d.data());
2545 d.data()[size] = '\0';
2546 }
2547}
2548
2560{
2561 d = DataPointer(1, 1);
2562 Q_CHECK_PTR(d.data());
2563 d.data()[0] = ch.unicode();
2564 d.data()[1] = '\0';
2565}
2566
2631static bool needsReallocate(const QString &str, qsizetype newSize)
2632{
2633 const auto capacityAtEnd = str.capacity() - str.data_ptr().freeSpaceAtBegin();
2634 return newSize > capacityAtEnd;
2635}
2636
2669{
2670 if (size < 0)
2671 size = 0;
2672
2673 if (d->needsDetach() || needsReallocate(*this, size))
2674 reallocData(size, QArrayData::Grow);
2675 d.size = size;
2676 if (d->allocatedCapacity())
2677 d.data()[size] = u'\0';
2678}
2679
2690void QString::resize(qsizetype newSize, QChar fillChar)
2691{
2692 const qsizetype oldSize = size();
2693 resize(newSize);
2694 const qsizetype difference = size() - oldSize;
2695 if (difference > 0)
2696 std::fill_n(d.data() + oldSize, difference, fillChar.unicode());
2697}
2698
2699
2715
2716
2785void QString::reallocData(qsizetype alloc, QArrayData::AllocationOption option)
2786{
2787 if (!alloc) {
2788 d = DataPointer::fromRawData(&_empty, 0);
2789 return;
2790 }
2791
2792 // don't use reallocate path when reducing capacity and there's free space
2793 // at the beginning: might shift data pointer outside of allocated space
2794 const bool cannotUseReallocate = d.freeSpaceAtBegin() > 0;
2795
2796 if (d->needsDetach() || cannotUseReallocate) {
2797 DataPointer dd(alloc, qMin(alloc, d.size), option);
2798 Q_CHECK_PTR(dd.data());
2799 if (dd.size > 0)
2800 ::memcpy(dd.data(), d.data(), dd.size * sizeof(QChar));
2801 dd.data()[dd.size] = 0;
2802 d = dd;
2803 } else {
2804 d->reallocate(alloc, option);
2805 }
2806}
2807
2808void QString::reallocGrowData(qsizetype n)
2809{
2810 if (!n) // expected to always allocate
2811 n = 1;
2812
2813 if (d->needsDetach()) {
2814 DataPointer dd(DataPointer::allocateGrow(d, n, QArrayData::GrowsAtEnd));
2815 Q_CHECK_PTR(dd.data());
2816 dd->copyAppend(d.data(), d.data() + d.size);
2817 dd.data()[dd.size] = 0;
2818 d = dd;
2819 } else {
2820 d->reallocate(d.constAllocatedCapacity() + n, QArrayData::Grow);
2821 }
2822}
2823
2838{
2839 d = other.d;
2840 return *this;
2841}
2842
2858{
2859 const qsizetype capacityAtEnd = capacity() - d.freeSpaceAtBegin();
2860 if (isDetached() && other.size() <= capacityAtEnd) { // assumes d->alloc == 0 -> !isDetached() (sharedNull)
2861 d.size = other.size();
2862 d.data()[other.size()] = 0;
2863 qt_from_latin1(d.data(), other.latin1(), other.size());
2864 } else {
2865 *this = fromLatin1(other.latin1(), other.size());
2866 }
2867 return *this;
2868}
2869
2902{
2903 return assign(1, ch);
2904}
2905
2917
2921
2969template <typename T>
2970static void insert_helper(QString &str, qsizetype i, const T &toInsert)
2971{
2972 auto &str_d = str.data_ptr();
2973 qsizetype difference = 0;
2974 if (Q_UNLIKELY(i > str_d.size))
2975 difference = i - str_d.size;
2976 const qsizetype oldSize = str_d.size;
2977 const qsizetype insert_size = toInsert.size();
2978 const qsizetype newSize = str_d.size + difference + insert_size;
2979 const auto side = i == 0 ? QArrayData::GrowsAtBeginning : QArrayData::GrowsAtEnd;
2980
2981 if (str_d.needsDetach() || needsReallocate(str, newSize)) {
2982 const auto cbegin = str.cbegin();
2983 const auto cend = str.cend();
2984 const auto insert_start = difference == 0 ? std::next(cbegin, i) : cend;
2985 QString other;
2986 // Using detachAndGrow() so that prepend optimization works and QStringBuilder
2987 // unittests pass
2988 other.data_ptr().detachAndGrow(side, newSize, nullptr, nullptr);
2989 other.append(QStringView(cbegin, insert_start));
2990 other.resize(i, u' ');
2991 other.append(toInsert);
2992 other.append(QStringView(insert_start, cend));
2993 str.swap(other);
2994 return;
2995 }
2996
2997 str_d.detachAndGrow(side, difference + insert_size, nullptr, nullptr);
2998 Q_CHECK_PTR(str_d.data());
2999 str.resize(newSize);
3000
3001 auto begin = str_d.begin();
3002 auto old_end = std::next(begin, oldSize);
3003 std::fill_n(old_end, difference, u' ');
3004 auto insert_start = std::next(begin, i);
3005 if (difference == 0)
3006 std::move_backward(insert_start, old_end, str_d.end());
3007
3008 using Char = std::remove_cv_t<typename T::value_type>;
3009 if constexpr(std::is_same_v<Char, QChar>)
3010 std::copy_n(reinterpret_cast<const char16_t *>(toInsert.data()), insert_size, insert_start);
3011 else if constexpr (std::is_same_v<Char, char16_t>)
3012 std::copy_n(toInsert.data(), insert_size, insert_start);
3013 else if constexpr (std::is_same_v<Char, char>)
3014 qt_from_latin1(insert_start, toInsert.data(), insert_size);
3015}
3016
3026{
3027 const char *s = str.latin1();
3028 if (i < 0 || !s || !(*s))
3029 return *this;
3030
3031 insert_helper(*this, i, str);
3032 return *this;
3033}
3034
3049{
3050 auto insert_size = s.size();
3051 if (i < 0 || insert_size <= 0)
3052 return *this;
3053
3054 qsizetype difference = 0;
3055 if (Q_UNLIKELY(i > d.size))
3056 difference = i - d.size;
3057
3058 const qsizetype newSize = d.size + difference + insert_size;
3059
3060 if (d.needsDetach() || needsReallocate(*this, newSize)) {
3061 const auto cbegin = this->cbegin();
3062 const auto insert_start = difference == 0 ? std::next(cbegin, i) : cend();
3063 QString other;
3064 other.reserve(newSize);
3065 other.append(QStringView(cbegin, insert_start));
3066 if (difference > 0)
3067 other.resize(i, u' ');
3068 other.append(s);
3069 other.append(QStringView(insert_start, cend()));
3070 swap(other);
3071 return *this;
3072 }
3073
3074 if (i >= d.size) {
3075 d.detachAndGrow(QArrayData::GrowsAtEnd, difference + insert_size, nullptr, nullptr);
3076 Q_CHECK_PTR(d.data());
3077
3078 if (difference > 0)
3079 resize(i, u' ');
3080 append(s);
3081 } else {
3082 // Optimal insertion of Utf8 data is at the end, anywhere else could
3083 // potentially lead to moving characters twice if Utf8 data size
3084 // (variable-width) is less than the equivalent Utf16 data size
3085 QVarLengthArray<char16_t> buffer(insert_size); // ### optimize (QTBUG-108546)
3086 char16_t *b = QUtf8::convertToUnicode(buffer.data(), s);
3087 insert_helper(*this, i, QStringView(buffer.data(), b));
3088 }
3089
3090 return *this;
3091}
3092
3106{
3107 if (i < 0 || size <= 0)
3108 return *this;
3109
3110 // In case when data points into "this"
3111 if (!d->needsDetach() && QtPrivate::q_points_into_range(unicode, *this)) {
3112 QVarLengthArray copy(unicode, unicode + size);
3113 insert(i, copy.data(), size);
3114 } else {
3115 insert_helper(*this, i, QStringView(unicode, size));
3116 }
3117
3118 return *this;
3119}
3120
3133{
3134 if (i < 0)
3135 i += d.size;
3136 return insert(i, &ch, 1);
3137}
3138
3158{
3159 if (!str.isNull()) {
3160 if (isNull()) {
3161 if (Q_UNLIKELY(!str.d.isMutable()))
3162 assign(str); // fromRawData, so we do a deep copy
3163 else
3164 operator=(str);
3165 } else if (str.size()) {
3166 append(str.constData(), str.size());
3167 }
3168 }
3169 return *this;
3170}
3171
3187{
3188 if (str && len > 0) {
3189 static_assert(sizeof(QChar) == sizeof(char16_t), "Unexpected difference in sizes");
3190 // the following should be safe as QChar uses char16_t as underlying data
3191 const char16_t *char16String = reinterpret_cast<const char16_t *>(str);
3192 d->growAppend(char16String, char16String + len);
3193 d.data()[d.size] = u'\0';
3194 }
3195 return *this;
3196}
3197
3204{
3205 append_helper(*this, str);
3206 return *this;
3207}
3208
3216{
3217 append_helper(*this, str);
3218 return *this;
3219}
3220
3253{
3254 d.detachAndGrow(QArrayData::GrowsAtEnd, 1, nullptr, nullptr);
3255 d->copyAppend(1, ch.unicode());
3256 d.data()[d.size] = '\0';
3257 return *this;
3258}
3259
3407{
3408 if (s.size() <= capacity() && isDetached()) {
3409 const auto offset = d.freeSpaceAtBegin();
3410 if (offset)
3411 d.setBegin(d.begin() - offset);
3412 resize(0);
3413 s.visit([this](auto input) {
3414 this->append(input);
3415 });
3416 } else {
3417 *this = s.toString();
3418 }
3419 return *this;
3420}
3421
3422#ifndef QT_BOOTSTRAPPED
3423QString &QString::assign_helper(const char32_t *data, qsizetype len)
3424{
3425 // worst case: each char32_t requires a surrogate pair, so
3426 const auto requiredCapacity = len * 2;
3427 if (requiredCapacity <= capacity() && isDetached()) {
3428 const auto offset = d.freeSpaceAtBegin();
3429 if (offset)
3430 d.setBegin(d.begin() - offset);
3431 auto begin = reinterpret_cast<QChar *>(d.begin());
3432 auto ba = QByteArrayView(reinterpret_cast<const std::byte*>(data), len * sizeof(char32_t));
3435 d.size = end - begin;
3436 d.data()[d.size] = u'\0';
3437 } else {
3438 *this = QString::fromUcs4(data, len);
3439 }
3440 return *this;
3441}
3442#endif
3443
3459
3463
3467{
3468 if (pos < 0) // count from end of string
3469 pos += size();
3470
3471 if (size_t(pos) >= size_t(size()) || len <= 0)
3472 return *this;
3473
3474 len = std::min(len, size() - pos);
3475
3476 if (!d->isShared()) {
3477 d->erase(d.begin() + pos, len);
3478 d.data()[d.size] = u'\0';
3479 } else {
3480 // TODO: either reserve "size()", which is bigger than needed, or
3481 // modify the shrinking-erase docs of this method (since the size
3482 // of "copy" won't have any extra capacity any more)
3483 const qsizetype sz = size() - len;
3485 auto begin = d.begin();
3486 auto toRemove_start = d.begin() + pos;
3487 copy.d->copyRanges({{begin, toRemove_start},
3488 {toRemove_start + len, d.end()}});
3489 swap(copy);
3490 }
3491 return *this;
3492}
3493
3494template<typename T>
3495static void removeStringImpl(QString &s, const T &needle, Qt::CaseSensitivity cs)
3496{
3497 const auto needleSize = needle.size();
3498 if (!needleSize)
3499 return;
3500
3501 // avoid detach if nothing to do:
3502 qsizetype i = s.indexOf(needle, 0, cs);
3503 if (i < 0)
3504 return;
3505
3506 QString::DataPointer &dptr = s.data_ptr();
3507 auto begin = dptr.begin();
3508 auto end = dptr.end();
3509
3510 auto copyFunc = [&](auto &dst) {
3511 auto src = begin + i + needleSize;
3512 while (src < end) {
3513 i = s.indexOf(needle, std::distance(begin, src), cs);
3514 auto hit = i == -1 ? end : begin + i;
3515 dst = std::copy(src, hit, dst);
3516 src = hit + needleSize;
3517 }
3518 return dst;
3519 };
3520
3521 if (!dptr->needsDetach()) {
3522 auto dst = begin + i;
3523 dst = copyFunc(dst);
3524 s.truncate(std::distance(begin, dst));
3525 } else {
3527 auto copy_begin = copy.begin();
3528 auto dst = std::copy(begin, begin + i, copy_begin); // Chunk before the first hit
3529 dst = copyFunc(dst);
3530 copy.resize(std::distance(copy_begin, dst));
3531 s.swap(copy);
3532 }
3533}
3534
3548{
3549 const auto s = str.d.data();
3552 else
3554 return *this;
3555}
3556
3573{
3574 removeStringImpl(*this, str, cs);
3575 return *this;
3576}
3577
3628{
3629 const qsizetype idx = indexOf(ch, 0, cs);
3630 if (idx == -1)
3631 return *this;
3632
3633 const bool isCase = cs == Qt::CaseSensitive;
3634 ch = isCase ? ch : ch.toCaseFolded();
3635 auto match = [ch, isCase](QChar x) {
3636 return ch == (isCase ? x : x.toCaseFolded());
3637 };
3638
3639
3640 auto begin = d.begin();
3641 auto first_match = begin + idx;
3642 auto end = d.end();
3643 if (!d->isShared()) {
3644 auto it = std::remove_if(first_match, end, match);
3645 d->erase(it, std::distance(it, end));
3646 d.data()[d.size] = u'\0';
3647 } else {
3648 // Instead of detaching, create a new string and copy all characters except for
3649 // the ones we're removing
3650 // TODO: size() is more than the needed since "copy" would be shorter
3652 auto dst = copy.d.begin();
3653 auto it = std::copy(begin, first_match, dst); // Chunk before idx
3654 it = std::remove_copy_if(first_match + 1, end, it, match);
3655 copy.d.size = std::distance(dst, it);
3656 copy.d.data()[copy.d.size] = u'\0';
3657 *this = std::move(copy);
3658 }
3659 return *this;
3660}
3661
3692static void replace_with_copy(QString &str, QSpan<size_t> indices, qsizetype blen,
3693 QStringView after)
3694{
3695 const qsizetype alen = after.size();
3696 const char16_t *after_b = after.utf16();
3697
3698 const QString::DataPointer &str_d = str.data_ptr();
3699 auto src_start = str_d.begin();
3700 const qsizetype newSize = str_d.size + indices.size() * (alen - blen);
3701 QString copy{ newSize, Qt::Uninitialized };
3702 QString::DataPointer &copy_d = copy.data_ptr();
3703 auto dst = copy_d.begin();
3704 for (size_t index : indices) {
3705 auto hit = str_d.begin() + index;
3706 dst = std::copy(src_start, hit, dst);
3707 dst = std::copy_n(after_b, alen, dst);
3708 src_start = hit + blen;
3709 }
3710 dst = std::copy(src_start, str_d.end(), dst);
3711 str.swap(copy);
3712}
3713
3714// No detaching or reallocation is needed
3715static void replace_in_place(QString &str, QSpan<size_t> indices,
3716 qsizetype blen, QStringView after)
3717{
3718 const qsizetype alen = after.size();
3719 const char16_t *after_b = after.utf16();
3720 const char16_t *after_e = after.utf16() + after.size();
3721
3722 if (blen == alen) { // Replace in place
3723 for (size_t index : indices)
3724 std::copy_n(after_b, alen, str.data_ptr().begin() + index);
3725 } else if (blen > alen) { // Replace from front
3726 char16_t *begin = str.data_ptr().begin();
3727 char16_t *hit = begin + indices.front();
3728 char16_t *to = hit;
3729 to = std::copy_n(after_b, alen, to);
3730 char16_t *movestart = hit + blen;
3731 for (size_t index : indices.sliced(1)) {
3732 hit = begin + index;
3733 to = std::move(movestart, hit, to);
3734 to = std::copy_n(after_b, alen, to);
3735 movestart = hit + blen;
3736 }
3737 to = std::move(movestart, str.data_ptr().end(), to);
3738 str.resize(std::distance(begin, to));
3739 } else { // blen < alen, Replace from back
3740 const qsizetype oldSize = str.data_ptr().size;
3741 const qsizetype adjust = indices.size() * (alen - blen);
3742 const qsizetype newSize = oldSize + adjust;
3743
3744 str.resize(newSize);
3745 char16_t *begin = str.data_ptr().begin();
3746 char16_t *moveend = begin + oldSize;
3747 char16_t *to = str.data_ptr().end();
3748
3749 for (auto it = indices.rbegin(), end = indices.rend(); it != end; ++it) {
3750 char16_t *hit = begin + *it;
3751 char16_t *movestart = hit + blen;
3752 to = std::move_backward(movestart, moveend, to);
3753 to = std::copy_backward(after_b, after_e, to);
3754 moveend = hit;
3755 }
3756 }
3757}
3758
3759static void replace_helper(QString &str, QSpan<size_t> indices, qsizetype blen, QStringView after)
3760{
3761 const qsizetype oldSize = str.data_ptr().size;
3762 const qsizetype adjust = indices.size() * (after.size() - blen);
3763 const qsizetype newSize = oldSize + adjust;
3764 if (str.data_ptr().needsDetach() || needsReallocate(str, newSize)) {
3765 replace_with_copy(str, indices, blen, after);
3766 return;
3767 }
3768
3769 if (QtPrivate::q_points_into_range(after.begin(), str))
3770 // Copy after if it lies inside our own d.b area (which we could
3771 // possibly invalidate via a realloc or modify by replacement)
3772 replace_in_place(str, indices, blen, QVarLengthArray(after.begin(), after.end()));
3773 else
3774 replace_in_place(str, indices, blen, after);
3775}
3776
3794{
3795 return replace(pos, len, after.constData(), after.size());
3796}
3797
3806{
3807 if (size_t(pos) > size_t(this->size()))
3808 return *this;
3809 if (len > this->size() - pos)
3810 len = this->size() - pos;
3811
3812 size_t index = pos;
3813 replace_helper(*this, QSpan(&index, 1), len, QStringView{after, alen});
3814 return *this;
3815}
3816
3825{
3826 return replace(pos, len, &after, 1);
3827}
3828
3847{
3848 return replace(before.constData(), before.size(), after.constData(), after.size(), cs);
3849}
3850
3862 const QChar *after, qsizetype alen,
3864{
3865 if (d.size == 0) {
3866 if (blen)
3867 return *this;
3868 } else {
3869 if (cs == Qt::CaseSensitive && before == after && blen == alen)
3870 return *this;
3871 }
3872 if (alen == 0 && blen == 0)
3873 return *this;
3874 if (alen == 1 && blen == 1)
3875 return replace(*before, *after, cs);
3876
3877 QStringMatcher matcher(before, blen, cs);
3878
3879 qsizetype index = 0;
3880
3881 QVarLengthArray<size_t> indices;
3882 while ((index = matcher.indexIn(*this, index)) != -1) {
3883 indices.push_back(index);
3884 if (blen) // Step over before:
3885 index += blen;
3886 else // Only count one instance of empty between any two characters:
3887 index++;
3888 }
3889 if (indices.isEmpty())
3890 return *this;
3891
3892 replace_helper(*this, indices, blen, QStringView{after, alen});
3893 return *this;
3894}
3895
3904{
3905 if (after.size() == 0)
3906 return remove(ch, cs);
3907
3908 if (after.size() == 1)
3909 return replace(ch, after.front(), cs);
3910
3911 if (size() == 0)
3912 return *this;
3913
3914 const char16_t cc = (cs == Qt::CaseSensitive ? ch.unicode() : ch.toCaseFolded().unicode());
3915
3916 QVarLengthArray<size_t> indices;
3917 if (cs == Qt::CaseSensitive) {
3918 const char16_t *begin = d.begin();
3919 const char16_t *end = d.end();
3921 const char16_t *hit = nullptr;
3922 while ((hit = QtPrivate::qustrchr(view, cc)) != end) {
3923 indices.push_back(std::distance(begin, hit));
3924 view = QStringView(std::next(hit), end);
3925 }
3926 } else {
3927 for (qsizetype i = 0; i < d.size; ++i)
3928 if (QChar::toCaseFolded(d.data()[i]) == cc)
3929 indices.push_back(i);
3930 }
3931 if (indices.isEmpty())
3932 return *this;
3933
3934 replace_helper(*this, indices, 1, after);
3935 return *this;
3936}
3937
3946{
3947 const qsizetype idx = indexOf(before, 0, cs);
3948 if (idx == -1)
3949 return *this;
3950
3951 const char16_t achar = after.unicode();
3952 char16_t bchar = before.unicode();
3953
3954 auto matchesCIS = [](char16_t beforeChar) {
3955 return [beforeChar](char16_t ch) { return foldAndCompare(ch, beforeChar); };
3956 };
3957
3958 auto hit = d.begin() + idx;
3959 if (!d.needsDetach()) {
3960 *hit++ = achar;
3961 if (cs == Qt::CaseSensitive) {
3962 std::replace(hit, d.end(), bchar, achar);
3963 } else {
3964 bchar = foldCase(bchar);
3965 std::replace_if(hit, d.end(), matchesCIS(bchar), achar);
3966 }
3967 } else {
3969 auto dest = std::copy(d.begin(), hit, other.d.begin());
3970 *dest++ = achar;
3971 ++hit;
3972 if (cs == Qt::CaseSensitive) {
3973 std::replace_copy(hit, d.end(), dest, bchar, achar);
3974 } else {
3975 bchar = foldCase(bchar);
3976 std::replace_copy_if(hit, d.end(), dest, matchesCIS(bchar), achar);
3977 }
3978
3979 swap(other);
3980 }
3981 return *this;
3982}
3983
3997{
3998 const qsizetype alen = after.size();
3999 const qsizetype blen = before.size();
4000 if (blen == 1 && alen == 1)
4001 return replace(before.front(), after.front(), cs);
4002
4003 QVarLengthArray<char16_t> a = qt_from_latin1_to_qvla(after);
4004 QVarLengthArray<char16_t> b = qt_from_latin1_to_qvla(before);
4005 return replace((const QChar *)b.data(), blen, (const QChar *)a.data(), alen, cs);
4006}
4007
4021{
4022 const qsizetype blen = before.size();
4023 if (blen == 1 && after.size() == 1)
4024 return replace(before.front(), after.front(), cs);
4025
4026 QVarLengthArray<char16_t> b = qt_from_latin1_to_qvla(before);
4027 return replace((const QChar *)b.data(), blen, after.constData(), after.d.size, cs);
4028}
4029
4042{
4043 const qsizetype alen = after.size();
4044 if (before.size() == 1 && alen == 1)
4045 return replace(before.front(), after.front(), cs);
4046
4047 QVarLengthArray<char16_t> a = qt_from_latin1_to_qvla(after);
4048 return replace(before.constData(), before.d.size, (const QChar *)a.data(), alen, cs);
4049}
4050
4063{
4064 const qsizetype alen = after.size();
4065 if (alen == 1)
4066 return replace(c, after.front(), cs);
4067
4068 QVarLengthArray<char16_t> a = qt_from_latin1_to_qvla(after);
4069 return replace(&c, 1, (const QChar *)a.data(), alen, cs);
4070}
4071
4483{
4484 return QtPrivate::findString(QStringView(unicode(), size()), from, QStringView(str.unicode(), str.size()), cs);
4485}
4486
4518{
4519 return QtPrivate::findString(QStringView(unicode(), size()), from, str, cs);
4520}
4521
4552{
4553 return QtPrivate::lastIndexOf(QStringView(*this), from, str, cs);
4554}
4555
4603
4670#if QT_CONFIG(regularexpression)
4671struct QStringCapture
4672{
4673 qsizetype pos;
4674 qsizetype len;
4675 int no;
4676};
4677Q_DECLARE_TYPEINFO(QStringCapture, Q_PRIMITIVE_TYPE);
4678
4697QString &QString::replace(const QRegularExpression &re, const QString &after)
4698{
4699 if (!re.isValid()) {
4700 qtWarnAboutInvalidRegularExpression(re.pattern(), "QString::replace");
4701 return *this;
4702 }
4703
4704 const QString copy(*this);
4706 if (!iterator.hasNext()) // no matches at all
4707 return *this;
4708
4709 reallocData(d.size, QArrayData::KeepSize);
4710
4711 qsizetype numCaptures = re.captureCount();
4712
4713 // 1. build the backreferences list, holding where the backreferences
4714 // are in the replacement string
4715 QVarLengthArray<QStringCapture> backReferences;
4716 const qsizetype al = after.size();
4717 const QChar *ac = after.unicode();
4718
4719 for (qsizetype i = 0; i < al - 1; i++) {
4720 if (ac[i] == u'\\') {
4721 int no = ac[i + 1].digitValue();
4722 if (no > 0 && no <= numCaptures) {
4723 QStringCapture backReference;
4724 backReference.pos = i;
4725 backReference.len = 2;
4726
4727 if (i < al - 2) {
4728 int secondDigit = ac[i + 2].digitValue();
4729 if (secondDigit != -1 && ((no * 10) + secondDigit) <= numCaptures) {
4730 no = (no * 10) + secondDigit;
4731 ++backReference.len;
4732 }
4733 }
4734
4735 backReference.no = no;
4736 backReferences.append(backReference);
4737 }
4738 }
4739 }
4740
4741 // 2. iterate on the matches. For every match, copy in chunks
4742 // - the part before the match
4743 // - the after string, with the proper replacements for the backreferences
4744
4745 qsizetype newLength = 0; // length of the new string, with all the replacements
4746 qsizetype lastEnd = 0;
4747 QVarLengthArray<QStringView> chunks;
4748 const QStringView copyView{ copy }, afterView{ after };
4749 while (iterator.hasNext()) {
4751 qsizetype len;
4752 // add the part before the match
4753 len = match.capturedStart() - lastEnd;
4754 if (len > 0) {
4755 chunks << copyView.mid(lastEnd, len);
4756 newLength += len;
4757 }
4758
4759 lastEnd = 0;
4760 // add the after string, with replacements for the backreferences
4761 for (const QStringCapture &backReference : std::as_const(backReferences)) {
4762 // part of "after" before the backreference
4763 len = backReference.pos - lastEnd;
4764 if (len > 0) {
4765 chunks << afterView.mid(lastEnd, len);
4766 newLength += len;
4767 }
4768
4769 // backreference itself
4770 len = match.capturedLength(backReference.no);
4771 if (len > 0) {
4772 chunks << copyView.mid(match.capturedStart(backReference.no), len);
4773 newLength += len;
4774 }
4775
4776 lastEnd = backReference.pos + backReference.len;
4777 }
4778
4779 // add the last part of the after string
4780 len = afterView.size() - lastEnd;
4781 if (len > 0) {
4782 chunks << afterView.mid(lastEnd, len);
4783 newLength += len;
4784 }
4785
4786 lastEnd = match.capturedEnd();
4787 }
4788
4789 // 3. trailing string after the last match
4790 if (copyView.size() > lastEnd) {
4791 chunks << copyView.mid(lastEnd);
4792 newLength += copyView.size() - lastEnd;
4793 }
4794
4795 // 4. assemble the chunks together
4796 resize(newLength);
4797 qsizetype i = 0;
4798 QChar *uc = data();
4799 for (const QStringView &chunk : std::as_const(chunks)) {
4800 qsizetype len = chunk.size();
4801 memcpy(uc + i, chunk.constData(), len * sizeof(QChar));
4802 i += len;
4803 }
4804
4805 return *this;
4806}
4807#endif // QT_CONFIG(regularexpression)
4808
4819{
4820 return QtPrivate::count(QStringView(unicode(), size()), QStringView(str.unicode(), str.size()), cs);
4821}
4822
4834{
4835 return QtPrivate::count(QStringView(unicode(), size()), ch, cs);
4836}
4837
4849{
4850 return QtPrivate::count(*this, str, cs);
4851}
4852
4895#if QT_CONFIG(regularexpression)
4912{
4913 return QtPrivate::indexOf(QStringView(*this), this, re, from, rmatch);
4914}
4915
4948{
4949 return QtPrivate::lastIndexOf(QStringView(*this), this, re, from, rmatch);
4950}
4951
4987{
4988 return QtPrivate::contains(QStringView(*this), this, re, rmatch);
4989}
4990
5010{
5011 return QtPrivate::count(QStringView(*this), re);
5012}
5013#endif // QT_CONFIG(regularexpression)
5014
5015#if QT_DEPRECATED_SINCE(6, 4)
5022#endif
5023
5089{
5090 const QList<QStringView> sections = QStringView{ *this }.split(
5091 sep, Qt::KeepEmptyParts, (flags & SectionCaseInsensitiveSeps) ? Qt::CaseInsensitive : Qt::CaseSensitive);
5092 const qsizetype sectionsSize = sections.size();
5093 if (!(flags & SectionSkipEmpty)) {
5094 if (start < 0)
5095 start += sectionsSize;
5096 if (end < 0)
5097 end += sectionsSize;
5098 } else {
5099 qsizetype skip = 0;
5100 for (qsizetype k = 0; k < sectionsSize; ++k) {
5101 if (sections.at(k).isEmpty())
5102 skip++;
5103 }
5104 if (start < 0)
5105 start += sectionsSize - skip;
5106 if (end < 0)
5107 end += sectionsSize - skip;
5108 }
5109 if (start >= sectionsSize || end < 0 || start > end)
5110 return QString();
5111
5112 QString ret;
5113 qsizetype first_i = start, last_i = end;
5114 for (qsizetype x = 0, i = 0; x <= end && i < sectionsSize; ++i) {
5115 const QStringView &section = sections.at(i);
5116 const bool empty = section.isEmpty();
5117 if (x >= start) {
5118 if (x == start)
5119 first_i = i;
5120 if (x == end)
5121 last_i = i;
5122 if (x > start && i > 0)
5123 ret += sep;
5124 ret += section;
5125 }
5126 if (!empty || !(flags & SectionSkipEmpty))
5127 x++;
5128 }
5129 if ((flags & SectionIncludeLeadingSep) && first_i > 0)
5130 ret.prepend(sep);
5131 if ((flags & SectionIncludeTrailingSep) && last_i < sectionsSize - 1)
5132 ret += sep;
5133 return ret;
5134}
5135
5136#if QT_CONFIG(regularexpression)
5137class qt_section_chunk {
5138public:
5139 qt_section_chunk() {}
5140 qt_section_chunk(qsizetype l, QStringView s) : length(l), string(std::move(s)) {}
5143};
5144Q_DECLARE_TYPEINFO(qt_section_chunk, Q_RELOCATABLE_TYPE);
5145
5146static QString extractSections(QSpan<qt_section_chunk> sections, qsizetype start, qsizetype end,
5147 QString::SectionFlags flags)
5148{
5149 const qsizetype sectionsSize = sections.size();
5150
5152 if (start < 0)
5153 start += sectionsSize;
5154 if (end < 0)
5155 end += sectionsSize;
5156 } else {
5157 qsizetype skip = 0;
5158 for (qsizetype k = 0; k < sectionsSize; ++k) {
5159 const qt_section_chunk &section = sections[k];
5160 if (section.length == section.string.size())
5161 skip++;
5162 }
5163 if (start < 0)
5164 start += sectionsSize - skip;
5165 if (end < 0)
5166 end += sectionsSize - skip;
5167 }
5168 if (start >= sectionsSize || end < 0 || start > end)
5169 return QString();
5170
5171 QString ret;
5172 qsizetype x = 0;
5173 qsizetype first_i = start, last_i = end;
5174 for (qsizetype i = 0; x <= end && i < sectionsSize; ++i) {
5175 const qt_section_chunk &section = sections[i];
5176 const bool empty = (section.length == section.string.size());
5177 if (x >= start) {
5178 if (x == start)
5179 first_i = i;
5180 if (x == end)
5181 last_i = i;
5182 if (x != start)
5183 ret += section.string;
5184 else
5185 ret += section.string.mid(section.length);
5186 }
5187 if (!empty || !(flags & QString::SectionSkipEmpty))
5188 x++;
5189 }
5190
5191 if ((flags & QString::SectionIncludeLeadingSep) && first_i >= 0) {
5192 const qt_section_chunk &section = sections[first_i];
5193 ret.prepend(section.string.left(section.length));
5194 }
5195
5197 && last_i < sectionsSize - 1) {
5198 const qt_section_chunk &section = sections[last_i + 1];
5199 ret += section.string.left(section.length);
5200 }
5201
5202 return ret;
5203}
5204
5220{
5221 if (!re.isValid()) {
5222 qtWarnAboutInvalidRegularExpression(re.pattern(), "QString::section");
5223 return QString();
5224 }
5225
5226 const QChar *uc = unicode();
5227 if (!uc)
5228 return QString();
5229
5231 if (flags & SectionCaseInsensitiveSeps)
5232 sep.setPatternOptions(sep.patternOptions() | QRegularExpression::CaseInsensitiveOption);
5233
5234 QVarLengthArray<qt_section_chunk> sections;
5235 qsizetype n = size(), m = 0, last_m = 0, last_len = 0;
5236 QRegularExpressionMatchIterator iterator = sep.globalMatch(*this);
5237 while (iterator.hasNext()) {
5239 m = match.capturedStart();
5240 sections.append(qt_section_chunk(last_len, QStringView{ *this }.sliced(last_m, m - last_m)));
5241 last_m = m;
5242 last_len = match.capturedLength();
5243 }
5244 sections.append(qt_section_chunk(last_len, QStringView{ *this }.sliced(last_m, n - last_m)));
5245
5246 return extractSections(sections, start, end, flags);
5247}
5248#endif // QT_CONFIG(regularexpression)
5249
5301{
5303 qsizetype l = n;
5304 using namespace QtPrivate;
5305 switch (QContainerImplHelper::mid(size(), &p, &l)) {
5306 case QContainerImplHelper::Null:
5307 return QString();
5308 case QContainerImplHelper::Empty:
5309 return QString(DataPointer::fromRawData(&_empty, 0));
5310 case QContainerImplHelper::Full:
5311 return *this;
5312 case QContainerImplHelper::Subset:
5313 return sliced(p, l);
5314 }
5315 Q_UNREACHABLE_RETURN(QString());
5316}
5317
5319{
5321 qsizetype l = n;
5322 using namespace QtPrivate;
5323 switch (QContainerImplHelper::mid(size(), &p, &l)) {
5324 case QContainerImplHelper::Null:
5325 return QString();
5326 case QContainerImplHelper::Empty:
5327 resize(0); // keep capacity if we've reserve()d
5328 [[fallthrough]];
5329 case QContainerImplHelper::Full:
5330 return std::move(*this);
5331 case QContainerImplHelper::Subset:
5332 return std::move(*this).sliced(p, l);
5333 }
5334 Q_UNREACHABLE_RETURN(QString());
5335}
5336
5381QString QString::sliced_helper(QString &str, qsizetype pos, qsizetype n)
5382{
5383 if (n == 0)
5384 return QString(DataPointer::fromRawData(&_empty, 0));
5385 DataPointer d = std::move(str.d).sliced(pos, n);
5386 d.data()[n] = 0;
5387 return QString(std::move(d));
5388}
5389
5456{
5457 return qt_starts_with_impl(QStringView(*this), QStringView(s), cs);
5458}
5459
5464{
5465 return qt_starts_with_impl(QStringView(*this), s, cs);
5466}
5467
5475{
5476 if (!size())
5477 return false;
5478 if (cs == Qt::CaseSensitive)
5479 return at(0) == c;
5480 return foldCase(at(0)) == foldCase(c);
5481}
5482
5507{
5508 return qt_ends_with_impl(QStringView(*this), QStringView(s), cs);
5509}
5510
5527{
5528 return qt_ends_with_impl(QStringView(*this), s, cs);
5529}
5530
5538{
5539 if (!size())
5540 return false;
5541 if (cs == Qt::CaseSensitive)
5542 return at(size() - 1) == c;
5543 return foldCase(at(size() - 1)) == foldCase(c);
5544}
5545
5547{
5549 while (it.hasNext()) {
5550 const char32_t uc = it.next();
5551 if (qGetProp(uc)->cases[c].diff)
5552 return false;
5553 }
5554 return true;
5555}
5556
5558{
5560}
5561
5563{
5565}
5566
5581{
5583}
5584
5599{
5601}
5602
5604
5605QByteArray QString::toLatin1_helper(const QString &string)
5606{
5607 return qt_convert_to_latin1(string);
5608}
5609
5621{
5622 return string.visit([] (auto string) { return string.toString(); });
5623}
5624
5641
5644{
5645 if (Q_UNLIKELY(string.isNull()))
5646 return QByteArray();
5647
5649
5650 // since we own the only copy, we're going to const_cast the constData;
5651 // that avoids an unnecessary call to detach() and expansion code that will never get used
5652 qt_to_latin1(reinterpret_cast<uchar *>(const_cast<char *>(ba.constData())),
5653 string.utf16(), string.size());
5654 return ba;
5655}
5656
5657QByteArray QString::toLatin1_helper_inplace(QString &s)
5658{
5659 if (!s.isDetached())
5660 return qt_convert_to_latin1(s);
5661
5662 // We can return our own buffer to the caller.
5663 // Conversion to Latin-1 always shrinks the buffer by half.
5664 // This relies on the fact that we use QArrayData for everything behind the scenes
5665
5666 // First, do the in-place conversion. Since isDetached() == true, the data
5667 // was allocated by QArrayData, so the null terminator must be there.
5668 qsizetype length = s.size();
5669 char16_t *sdata = s.d->data();
5670 Q_ASSERT(sdata[length] == u'\0');
5671 qt_to_latin1(reinterpret_cast<uchar *>(sdata), sdata, length + 1);
5672
5673 // Move the internals over to the byte array.
5674 // Kids, avert your eyes. Don't try this at home.
5675 auto ba_d = std::move(s.d).reinterpreted<char>();
5676
5677 // Some sanity checks
5678 Q_ASSERT(ba_d.d->allocatedCapacity() >= ba_d.size);
5679 Q_ASSERT(s.isNull());
5680 Q_ASSERT(s.isEmpty());
5681 Q_ASSERT(s.constData() == QString().constData());
5682
5683 return QByteArray(std::move(ba_d));
5684}
5685
5686// QLatin1 methods that use helpers from qstring.cpp
5688{
5689 const qsizetype len = in.size();
5690 qt_from_latin1(out, in.data(), len);
5691 return std::next(out, len);
5692}
5693
5695{
5696 const qsizetype len = in.size();
5697 qt_to_latin1(reinterpret_cast<uchar *>(out), in.utf16(), len);
5698 return out + len;
5699}
5700
5714
5730QByteArray QString::toLocal8Bit_helper(const QChar *data, qsizetype size)
5731{
5733}
5734
5736{
5737 if (string.isNull())
5738 return QByteArray();
5740 return fromUtf16(string);
5741}
5742
5762
5764
5776QByteArray QString::toUtf8_helper(const QString &str)
5777{
5778 return qt_convert_to_utf8(str);
5779}
5780
5782{
5783 if (str.isNull())
5784 return QByteArray();
5785
5787}
5788
5805
5806static QList<uint> qt_convert_to_ucs4(QStringView string);
5807
5823QList<uint> QString::toUcs4() const
5824{
5825 return qt_convert_to_ucs4(*this);
5826}
5827
5828static QList<uint> qt_convert_to_ucs4(QStringView string)
5829{
5830 QList<uint> v(string.size());
5831 uint *a = const_cast<uint*>(v.constData());
5832 QStringIterator it(string);
5833 while (it.hasNext())
5834 *a++ = it.next();
5835 v.resize(a - v.constData());
5836 return v;
5837}
5838
5857{
5858 return qt_convert_to_ucs4(string);
5859}
5860
5872{
5873 DataPointer d;
5874 if (!ba.data()) {
5875 // nothing to do
5876 } else if (ba.size() == 0) {
5877 d = DataPointer::fromRawData(&_empty, 0);
5878 } else {
5879 d = DataPointer(ba.size(), ba.size());
5880 Q_CHECK_PTR(d.data());
5881 d.data()[ba.size()] = '\0';
5882 char16_t *dst = d.data();
5883
5884 qt_from_latin1(dst, ba.data(), size_t(ba.size()));
5885 }
5886 return QString(std::move(d));
5887}
5888
5950{
5951 if (ba.isNull())
5952 return QString();
5953 if (ba.isEmpty())
5954 return QString(DataPointer::fromRawData(&_empty, 0));
5956 return toUtf16(ba);
5957}
5958
6019{
6020 if (ba.isNull())
6021 return QString();
6022 if (ba.isEmpty())
6023 return QString(DataPointer::fromRawData(&_empty, 0));
6025}
6026
6027#ifndef QT_BOOTSTRAPPED
6045QString QString::fromUtf16(const char16_t *unicode, qsizetype size)
6046{
6047 if (!unicode)
6048 return QString();
6049 if (size < 0)
6050 size = QtPrivate::qustrlen(unicode);
6052 return toUtf16(QByteArrayView(reinterpret_cast<const char *>(unicode), size * 2));
6053}
6054
6077QString QString::fromUcs4(const char32_t *unicode, qsizetype size)
6078{
6079 if (!unicode)
6080 return QString();
6081 if (size < 0) {
6082 size = 0;
6083 while (unicode[size] != 0)
6084 ++size;
6085 }
6087 return toUtf16(QByteArrayView(reinterpret_cast<const char *>(unicode), size * 4));
6088}
6089#endif // !QT_BOOTSTRAPPED
6090
6101{
6102 resize(size);
6103 if (unicode && size)
6104 memcpy(d.data(), unicode, size * sizeof(QChar));
6105 return *this;
6106}
6107
6140QString QString::simplified_helper(const QString &str)
6141{
6143}
6144
6145QString QString::simplified_helper(QString &str)
6146{
6148}
6149
6150namespace {
6151 template <typename StringView>
6152 StringView qt_trimmed(StringView s) noexcept
6153 {
6155 return StringView{begin, end};
6156 }
6157}
6158
6175{
6176 return qt_trimmed(s);
6177}
6178
6180{
6181 return qt_trimmed(s);
6182}
6183
6202QString QString::trimmed_helper(const QString &str)
6203{
6205}
6206
6207QString QString::trimmed_helper(QString &str)
6208{
6210}
6211
6320{
6321 if (pos < size())
6322 resize(pos);
6323}
6324
6325
6341{
6342 if (n > 0)
6343 resize(d.size - n);
6344}
6345
6359{
6360 resize(size < 0 ? d.size : size);
6361 if (d.size)
6362 std::fill(d.data(), d.data() + d.size, ch.unicode());
6363 return *this;
6364}
6365
6608
6611
6665{
6666 return QtPrivate::compareStrings(*this, other, cs);
6667}
6668
6673int QString::compare_helper(const QChar *data1, qsizetype length1, const QChar *data2, qsizetype length2,
6674 Qt::CaseSensitivity cs) noexcept
6675{
6676 Q_ASSERT(length1 >= 0);
6677 Q_ASSERT(length2 >= 0);
6678 Q_ASSERT(data1 || length1 == 0);
6679 Q_ASSERT(data2 || length2 == 0);
6680 return QtPrivate::compareStrings(QStringView(data1, length1), QStringView(data2, length2), cs);
6681}
6682
6690{
6691 return QtPrivate::compareStrings(*this, other, cs);
6692}
6693
6698int QString::compare_helper(const QChar *data1, qsizetype length1, const char *data2, qsizetype length2,
6699 Qt::CaseSensitivity cs) noexcept
6700{
6701 Q_ASSERT(length1 >= 0);
6702 Q_ASSERT(data1 || length1 == 0);
6703 if (!data2)
6704 return qt_lencmp(length1, 0);
6705 if (Q_UNLIKELY(length2 < 0))
6706 length2 = qsizetype(strlen(data2));
6707 return QtPrivate::compareStrings(QStringView(data1, length1),
6708 QUtf8StringView(data2, length2), cs);
6709}
6710
6721bool comparesEqual(const QByteArrayView &lhs, const QChar &rhs) noexcept
6722{
6724}
6725
6727{
6728 const int res = QtPrivate::compareStrings(QUtf8StringView(lhs), QStringView(&rhs, 1));
6729 return Qt::compareThreeWay(res, 0);
6730}
6731
6732bool comparesEqual(const QByteArrayView &lhs, char16_t rhs) noexcept
6733{
6735}
6736
6737Qt::strong_ordering compareThreeWay(const QByteArrayView &lhs, char16_t rhs) noexcept
6738{
6739 const int res = QtPrivate::compareStrings(QUtf8StringView(lhs), QStringView(&rhs, 1));
6740 return Qt::compareThreeWay(res, 0);
6741}
6742
6743bool comparesEqual(const QByteArray &lhs, const QChar &rhs) noexcept
6744{
6746}
6747
6748Qt::strong_ordering compareThreeWay(const QByteArray &lhs, const QChar &rhs) noexcept
6749{
6750 const int res = QtPrivate::compareStrings(QUtf8StringView(lhs), QStringView(&rhs, 1));
6751 return Qt::compareThreeWay(res, 0);
6752}
6753
6754bool comparesEqual(const QByteArray &lhs, char16_t rhs) noexcept
6755{
6757}
6758
6759Qt::strong_ordering compareThreeWay(const QByteArray &lhs, char16_t rhs) noexcept
6760{
6761 const int res = QtPrivate::compareStrings(QUtf8StringView(lhs), QStringView(&rhs, 1));
6762 return Qt::compareThreeWay(res, 0);
6763}
6764
6769bool QT_FASTCALL QChar::equal_helper(QChar lhs, const char *rhs) noexcept
6770{
6772}
6773
6774int QT_FASTCALL QChar::compare_helper(QChar lhs, const char *rhs) noexcept
6775{
6777}
6778
6783bool QStringView::equal_helper(QStringView sv, const char *data, qsizetype len)
6784{
6785 Q_ASSERT(len >= 0);
6786 Q_ASSERT(data || len == 0);
6788}
6789
6794int QStringView::compare_helper(QStringView sv, const char *data, qsizetype len)
6795{
6796 Q_ASSERT(len >= 0);
6797 Q_ASSERT(data || len == 0);
6799}
6800
6805bool QLatin1StringView::equal_helper(QLatin1StringView s1, const char *s2, qsizetype len) noexcept
6806{
6807 // because qlatin1stringview.h can't include qutf8stringview.h
6808 Q_ASSERT(len >= 0);
6809 Q_ASSERT(s2 || len == 0);
6811}
6812
6817int QLatin1StringView::compare_helper(const QLatin1StringView &s1, const char *s2, qsizetype len) noexcept
6818{
6819 // because qlatin1stringview.h can't include qutf8stringview.h
6820 Q_ASSERT(len >= 0);
6821 Q_ASSERT(s2 || len == 0);
6823}
6824
6829int QLatin1StringView::compare_helper(const QChar *data1, qsizetype length1, QLatin1StringView s2,
6830 Qt::CaseSensitivity cs) noexcept
6831{
6832 Q_ASSERT(length1 >= 0);
6833 Q_ASSERT(data1 || length1 == 0);
6834 return QtPrivate::compareStrings(QStringView(data1, length1), s2, cs);
6835}
6836
6886#if !defined(CSTR_LESS_THAN)
6887#define CSTR_LESS_THAN 1
6888#define CSTR_EQUAL 2
6889#define CSTR_GREATER_THAN 3
6890#endif
6891
6908{
6909 return localeAwareCompare_helper(constData(), size(), other.constData(), other.size());
6910}
6911
6916int QString::localeAwareCompare_helper(const QChar *data1, qsizetype length1,
6917 const QChar *data2, qsizetype length2)
6918{
6919 Q_ASSERT(length1 >= 0);
6920 Q_ASSERT(data1 || length1 == 0);
6921 Q_ASSERT(length2 >= 0);
6922 Q_ASSERT(data2 || length2 == 0);
6923
6924 // do the right thing for null and empty
6925 if (length1 == 0 || length2 == 0)
6926 return QtPrivate::compareStrings(QStringView(data1, length1), QStringView(data2, length2),
6928
6929#if QT_CONFIG(icu)
6930 return QCollator::defaultCompare(QStringView(data1, length1), QStringView(data2, length2));
6931#else
6932 const QString lhs = QString::fromRawData(data1, length1).normalized(QString::NormalizationForm_C);
6933 const QString rhs = QString::fromRawData(data2, length2).normalized(QString::NormalizationForm_C);
6934# if defined(Q_OS_WIN)
6935 int res = CompareStringEx(LOCALE_NAME_USER_DEFAULT, 0, (LPWSTR)lhs.constData(), lhs.length(), (LPWSTR)rhs.constData(), rhs.length(), NULL, NULL, 0);
6936
6937 switch (res) {
6938 case CSTR_LESS_THAN:
6939 return -1;
6940 case CSTR_GREATER_THAN:
6941 return 1;
6942 default:
6943 return 0;
6944 }
6945# elif defined (Q_OS_DARWIN)
6946 // Use CFStringCompare for comparing strings on Mac. This makes Qt order
6947 // strings the same way as native applications do, and also respects
6948 // the "Order for sorted lists" setting in the International preferences
6949 // panel.
6950 const CFStringRef thisString =
6951 CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
6952 reinterpret_cast<const UniChar *>(lhs.constData()), lhs.length(), kCFAllocatorNull);
6953 const CFStringRef otherString =
6954 CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault,
6955 reinterpret_cast<const UniChar *>(rhs.constData()), rhs.length(), kCFAllocatorNull);
6956
6957 const int result = CFStringCompare(thisString, otherString, kCFCompareLocalized);
6958 CFRelease(thisString);
6959 CFRelease(otherString);
6960 return result;
6961# elif defined(Q_OS_UNIX)
6962 // declared in <string.h> (no better than QtPrivate::compareStrings() on Android, sadly)
6963 return strcoll(lhs.toLocal8Bit().constData(), rhs.toLocal8Bit().constData());
6964# else
6965# error "This case shouldn't happen"
6967# endif
6968#endif // !QT_CONFIG(icu)
6969}
6970
6971
6996{
6997 if (!d->isMutable()) {
6998 // ensure '\0'-termination for ::fromRawData strings
6999 const_cast<QString*>(this)->reallocData(d.size, QArrayData::KeepSize);
7000 }
7001 return reinterpret_cast<const ushort *>(d.data());
7002}
7003
7023{
7025 qsizetype len = size();
7026 qsizetype padlen = width - len;
7027 if (padlen > 0) {
7028 result.resize(len+padlen);
7029 if (len)
7030 memcpy(result.d.data(), d.data(), sizeof(QChar)*len);
7031 QChar *uc = (QChar*)result.d.data() + len;
7032 while (padlen--)
7033 * uc++ = fill;
7034 } else {
7035 if (truncate)
7036 result = left(width);
7037 else
7038 result = *this;
7039 }
7040 return result;
7041}
7042
7062{
7064 qsizetype len = size();
7065 qsizetype padlen = width - len;
7066 if (padlen > 0) {
7067 result.resize(len+padlen);
7068 QChar *uc = (QChar*)result.d.data();
7069 while (padlen--)
7070 * uc++ = fill;
7071 if (len)
7072 memcpy(static_cast<void *>(uc), static_cast<const void *>(d.data()), sizeof(QChar)*len);
7073 } else {
7074 if (truncate)
7075 result = left(width);
7076 else
7077 result = *this;
7078 }
7079 return result;
7080}
7081
7096/*
7097 \internal
7098 Converts the \a str string starting from the position pointed to by the \a
7099 it iterator, using the Unicode case traits \c Traits, and returns the
7100 result. The input string must not be empty (the convertCase function below
7101 guarantees that).
7102
7103 The string type \c{T} is also a template and is either \c{const QString} or
7104 \c{QString}. This function can do both copy-conversion and in-place
7105 conversion depending on the state of the \a str parameter:
7106 \list
7107 \li \c{T} is \c{const QString}: copy-convert
7108 \li \c{T} is \c{QString} and its refcount != 1: copy-convert
7109 \li \c{T} is \c{QString} and its refcount == 1: in-place convert
7110 \endlist
7111
7112 In copy-convert mode, the local variable \c{s} is detached from the input
7113 \a str. In the in-place convert mode, \a str is in moved-from state and
7114 \c{s} contains the only copy of the string, without reallocation (thus,
7115 \a it is still valid).
7116
7117 There is one pathological case left: when the in-place conversion needs to
7118 reallocate memory to grow the buffer. In that case, we need to adjust the \a
7119 it pointer.
7120 */
7121template <typename T>
7124{
7125 Q_ASSERT(!str.isEmpty());
7126 QString s = std::move(str); // will copy if T is const QString
7127 QChar *pp = s.begin() + it.index(); // will detach if necessary
7128
7129 do {
7130 const auto folded = fullConvertCase(it.next(), which);
7131 if (Q_UNLIKELY(folded.size() > 1)) {
7132 if (folded.chars[0] == *pp && folded.size() == 2) {
7133 // special case: only second actually changed (e.g. surrogate pairs),
7134 // avoid slow case
7135 ++pp;
7136 *pp++ = folded.chars[1];
7137 } else {
7138 // slow path: the string is growing
7139 qsizetype inpos = it.index() - 1;
7140 qsizetype outpos = pp - s.constBegin();
7141
7142 s.replace(outpos, 1, reinterpret_cast<const QChar *>(folded.data()), folded.size());
7143 pp = const_cast<QChar *>(s.constBegin()) + outpos + folded.size();
7144
7145 // Adjust the input iterator if we are performing an in-place conversion
7146 if constexpr (!std::is_const<T>::value)
7147 it = QStringIterator(s.constBegin(), inpos + folded.size(), s.constEnd());
7148 }
7149 } else {
7150 *pp++ = folded.chars[0];
7151 }
7152 } while (it.hasNext());
7153
7154 return s;
7155}
7156
7157template <typename T>
7159{
7160 const QChar *p = str.constBegin();
7161 const QChar *e = p + str.size();
7162
7163 // this avoids out of bounds check in the loop
7164 while (e != p && e[-1].isHighSurrogate())
7165 --e;
7166
7167 QStringIterator it(p, e);
7168 while (it.hasNext()) {
7169 const char32_t uc = it.next();
7170 if (qGetProp(uc)->cases[which].diff) {
7171 it.recede();
7172 return detachAndConvertCase(str, it, which);
7173 }
7174 }
7175 return std::move(str);
7176}
7177} // namespace QUnicodeTables
7178
7179QString QString::toLower_helper(const QString &str)
7180{
7182}
7183
7184QString QString::toLower_helper(QString &str)
7185{
7187}
7188
7196QString QString::toCaseFolded_helper(const QString &str)
7197{
7199}
7200
7201QString QString::toCaseFolded_helper(QString &str)
7202{
7204}
7205
7219QString QString::toUpper_helper(const QString &str)
7220{
7222}
7223
7224QString QString::toUpper_helper(QString &str)
7225{
7227}
7228
7263QString QString::asprintf(const char *cformat, ...)
7264{
7265 va_list ap;
7266 va_start(ap, cformat);
7267 const QString s = vasprintf(cformat, ap);
7268 va_end(ap);
7269 return s;
7270}
7271
7272static void append_utf8(QString &qs, const char *cs, qsizetype len)
7273{
7274 const qsizetype oldSize = qs.size();
7275 qs.resize(oldSize + len);
7276 const QChar *newEnd = QUtf8::convertToUnicode(qs.data() + oldSize, QByteArrayView(cs, len));
7277 qs.resize(newEnd - qs.constData());
7278}
7279
7280static uint parse_flag_characters(const char * &c) noexcept
7281{
7283 while (true) {
7284 switch (*c) {
7285 case '#':
7288 break;
7289 case '0': flags |= QLocaleData::ZeroPadded; break;
7290 case '-': flags |= QLocaleData::LeftAdjusted; break;
7291 case ' ': flags |= QLocaleData::BlankBeforePositive; break;
7292 case '+': flags |= QLocaleData::AlwaysShowSign; break;
7293 case '\'': flags |= QLocaleData::GroupDigits; break;
7294 default: return flags;
7295 }
7296 ++c;
7297 }
7298}
7299
7300static int parse_field_width(const char *&c, qsizetype size)
7301{
7303 const char *const stop = c + size;
7304
7305 // can't be negative - started with a digit
7306 // contains at least one digit
7307 auto [result, used] = qstrntoull(c, size, 10);
7308 c += used;
7309 if (used <= 0)
7310 return false;
7311 // preserve Qt 5.5 behavior of consuming all digits, no matter how many
7312 while (c < stop && isAsciiDigit(*c))
7313 ++c;
7314 return result < qulonglong(std::numeric_limits<int>::max()) ? int(result) : 0;
7315}
7316
7318
7319static inline bool can_consume(const char * &c, char ch) noexcept
7320{
7321 if (*c == ch) {
7322 ++c;
7323 return true;
7324 }
7325 return false;
7326}
7327
7328static LengthMod parse_length_modifier(const char * &c) noexcept
7329{
7330 switch (*c++) {
7331 case 'h': return can_consume(c, 'h') ? lm_hh : lm_h;
7332 case 'l': return can_consume(c, 'l') ? lm_ll : lm_l;
7333 case 'L': return lm_L;
7334 case 'j': return lm_j;
7335 case 'z':
7336 case 'Z': return lm_z;
7337 case 't': return lm_t;
7338 }
7339 --c; // don't consume *c - it wasn't a flag
7340 return lm_none;
7341}
7342
7357QString QString::vasprintf(const char *cformat, va_list ap)
7358{
7359 if (!cformat || !*cformat) {
7360 // Qt 1.x compat
7361 return fromLatin1("");
7362 }
7363
7364 // Parse cformat
7365
7367 const char *c = cformat;
7368 const char *formatEnd = cformat + qstrlen(cformat);
7369 for (;;) {
7370 // Copy non-escape chars to result
7371 const char *cb = c;
7372 while (*c != '\0' && *c != '%')
7373 c++;
7375
7376 if (*c == '\0')
7377 break;
7378
7379 // Found '%'
7380 const char *escape_start = c;
7381 ++c;
7382
7383 if (*c == '\0') {
7384 result.append(u'%'); // a % at the end of the string - treat as non-escape text
7385 break;
7386 }
7387 if (*c == '%') {
7388 result.append(u'%'); // %%
7389 ++c;
7390 continue;
7391 }
7392
7394
7395 if (*c == '\0') {
7396 result.append(QLatin1StringView(escape_start)); // incomplete escape, treat as non-escape text
7397 break;
7398 }
7399
7400 // Parse field width
7401 int width = -1; // -1 means unspecified
7402 if (isAsciiDigit(*c)) {
7403 width = parse_field_width(c, formatEnd - c);
7404 } else if (*c == '*') { // can't parse this in another function, not portably, at least
7405 width = va_arg(ap, int);
7406 if (width < 0)
7407 width = -1; // treat all negative numbers as unspecified
7408 ++c;
7409 }
7410
7411 if (*c == '\0') {
7412 result.append(QLatin1StringView(escape_start)); // incomplete escape, treat as non-escape text
7413 break;
7414 }
7415
7416 // Parse precision
7417 int precision = -1; // -1 means unspecified
7418 if (*c == '.') {
7419 ++c;
7420 precision = 0;
7421 if (isAsciiDigit(*c)) {
7422 precision = parse_field_width(c, formatEnd - c);
7423 } else if (*c == '*') { // can't parse this in another function, not portably, at least
7424 precision = va_arg(ap, int);
7425 if (precision < 0)
7426 precision = -1; // treat all negative numbers as unspecified
7427 ++c;
7428 }
7429 }
7430
7431 if (*c == '\0') {
7432 result.append(QLatin1StringView(escape_start)); // incomplete escape, treat as non-escape text
7433 break;
7434 }
7435
7436 const LengthMod length_mod = parse_length_modifier(c);
7437
7438 if (*c == '\0') {
7439 result.append(QLatin1StringView(escape_start)); // incomplete escape, treat as non-escape text
7440 break;
7441 }
7442
7443 // Parse the conversion specifier and do the conversion
7444 QString subst;
7445 switch (*c) {
7446 case 'd':
7447 case 'i': {
7448 qint64 i;
7449 switch (length_mod) {
7450 case lm_none: i = va_arg(ap, int); break;
7451 case lm_hh: i = va_arg(ap, int); break;
7452 case lm_h: i = va_arg(ap, int); break;
7453 case lm_l: i = va_arg(ap, long int); break;
7454 case lm_ll: i = va_arg(ap, qint64); break;
7455 case lm_j: i = va_arg(ap, long int); break;
7456
7457 /* ptrdiff_t actually, but it should be the same for us */
7458 case lm_z: i = va_arg(ap, qsizetype); break;
7459 case lm_t: i = va_arg(ap, qsizetype); break;
7460 default: i = 0; break;
7461 }
7462 subst = QLocaleData::c()->longLongToString(i, precision, 10, width, flags);
7463 ++c;
7464 break;
7465 }
7466 case 'o':
7467 case 'u':
7468 case 'x':
7469 case 'X': {
7470 quint64 u;
7471 switch (length_mod) {
7472 case lm_none: u = va_arg(ap, uint); break;
7473 case lm_hh: u = va_arg(ap, uint); break;
7474 case lm_h: u = va_arg(ap, uint); break;
7475 case lm_l: u = va_arg(ap, ulong); break;
7476 case lm_ll: u = va_arg(ap, quint64); break;
7477 case lm_t: u = va_arg(ap, size_t); break;
7478 case lm_z: u = va_arg(ap, size_t); break;
7479 default: u = 0; break;
7480 }
7481
7482 if (isAsciiUpper(*c))
7484
7485 int base = 10;
7486 switch (QtMiscUtils::toAsciiLower(*c)) {
7487 case 'o':
7488 base = 8; break;
7489 case 'u':
7490 base = 10; break;
7491 case 'x':
7492 base = 16; break;
7493 default: break;
7494 }
7495 subst = QLocaleData::c()->unsLongLongToString(u, precision, base, width, flags);
7496 ++c;
7497 break;
7498 }
7499 case 'E':
7500 case 'e':
7501 case 'F':
7502 case 'f':
7503 case 'G':
7504 case 'g':
7505 case 'A':
7506 case 'a': {
7507 double d;
7508 if (length_mod == lm_L)
7509 d = va_arg(ap, long double); // not supported - converted to a double
7510 else
7511 d = va_arg(ap, double);
7512
7513 if (isAsciiUpper(*c))
7515
7517 switch (QtMiscUtils::toAsciiLower(*c)) {
7518 case 'e': form = QLocaleData::DFExponent; break;
7519 case 'a': // not supported - decimal form used instead
7520 case 'f': form = QLocaleData::DFDecimal; break;
7521 case 'g': form = QLocaleData::DFSignificantDigits; break;
7522 default: break;
7523 }
7524 subst = QLocaleData::c()->doubleToString(d, precision, form, width, flags);
7525 ++c;
7526 break;
7527 }
7528 case 'c': {
7529 if (length_mod == lm_l)
7530 subst = QChar::fromUcs2(va_arg(ap, int));
7531 else
7532 subst = QLatin1Char((uchar) va_arg(ap, int));
7533 ++c;
7534 break;
7535 }
7536 case 's': {
7537 if (length_mod == lm_l) {
7538 const ushort *buff = va_arg(ap, const ushort*);
7539 const ushort *ch = buff;
7540 while (precision != 0 && *ch != 0) {
7541 ++ch;
7542 --precision;
7543 }
7544 subst.setUtf16(buff, ch - buff);
7545 } else if (precision == -1) {
7546 subst = QString::fromUtf8(va_arg(ap, const char*));
7547 } else {
7548 const char *buff = va_arg(ap, const char*);
7549 subst = QString::fromUtf8(buff, qstrnlen(buff, precision));
7550 }
7551 ++c;
7552 break;
7553 }
7554 case 'p': {
7555 void *arg = va_arg(ap, void*);
7556 const quint64 i = reinterpret_cast<quintptr>(arg);
7558 subst = QLocaleData::c()->unsLongLongToString(i, precision, 16, width, flags);
7559 ++c;
7560 break;
7561 }
7562 case 'n':
7563 switch (length_mod) {
7564 case lm_hh: {
7565 signed char *n = va_arg(ap, signed char*);
7566 *n = result.size();
7567 break;
7568 }
7569 case lm_h: {
7570 short int *n = va_arg(ap, short int*);
7571 *n = result.size();
7572 break;
7573 }
7574 case lm_l: {
7575 long int *n = va_arg(ap, long int*);
7576 *n = result.size();
7577 break;
7578 }
7579 case lm_ll: {
7580 qint64 *n = va_arg(ap, qint64*);
7581 *n = result.size();
7582 break;
7583 }
7584 default: {
7585 int *n = va_arg(ap, int*);
7586 *n = int(result.size());
7587 break;
7588 }
7589 }
7590 ++c;
7591 break;
7592
7593 default: // bad escape, treat as non-escape text
7594 for (const char *cc = escape_start; cc != c; ++cc)
7595 result.append(QLatin1Char(*cc));
7596 continue;
7597 }
7598
7600 result.append(subst.leftJustified(width));
7601 else
7602 result.append(subst.rightJustified(width));
7603 }
7604
7605 return result;
7606}
7607
7637template <typename Int>
7638static Int toIntegral(QStringView string, bool *ok, int base)
7639{
7640#if defined(QT_CHECK_RANGE)
7641 if (base != 0 && (base < 2 || base > 36)) {
7642 qWarning("QString::toIntegral: Invalid base (%d)", base);
7643 base = 10;
7644 }
7645#endif
7646
7647 QVarLengthArray<uchar> latin1(string.size());
7648 qt_to_latin1(latin1.data(), string.utf16(), string.size());
7649 QSimpleParsedNumber<Int> r;
7650 if constexpr (std::is_signed_v<Int>)
7652 else
7654 if (ok)
7655 *ok = r.ok();
7656 return r.result;
7657}
7658
7659qlonglong QString::toIntegral_helper(QStringView string, bool *ok, int base)
7660{
7661 return toIntegral<qlonglong>(string, ok, base);
7662}
7663
7693qulonglong QString::toIntegral_helper(QStringView string, bool *ok, uint base)
7694{
7695 return toIntegral<qulonglong>(string, ok, base);
7696}
7697
7904double QString::toDouble(bool *ok) const
7905{
7906 return QStringView(*this).toDouble(ok);
7907}
7908
7909double QStringView::toDouble(bool *ok) const
7910{
7911 QStringView string = qt_trimmed(*this);
7912 QVarLengthArray<uchar> latin1(string.size());
7913 qt_to_latin1(latin1.data(), string.utf16(), string.size());
7914 auto r = qt_asciiToDouble(reinterpret_cast<const char *>(latin1.data()), string.size());
7915 if (ok != nullptr)
7916 *ok = r.ok();
7917 return r.result;
7918}
7919
7950float QString::toFloat(bool *ok) const
7951{
7953}
7954
7955float QStringView::toFloat(bool *ok) const
7956{
7958}
7959
7995{
7996 return *this = number(n, base);
7997}
7998
8003{
8004 return *this = number(n, base);
8005}
8006
8027{
8028 return *this = number(n, format, precision);
8029}
8030
8067{
8068 return number(qlonglong(n), base);
8069}
8070
8077{
8078 return number(qulonglong(n), base);
8079}
8080
8085{
8086 return number(qlonglong(n), base);
8087}
8088
8093{
8094 return number(qulonglong(n), base);
8095}
8096
8101{
8102#if defined(QT_CHECK_RANGE)
8103 if (base < 2 || base > 36) {
8104 qWarning("QString::setNum: Invalid base (%d)", base);
8105 base = 10;
8106 }
8107#endif
8108 bool negative = n < 0;
8109 /*
8110 Negating std::numeric_limits<qlonglong>::min() hits undefined behavior, so
8111 taking an absolute value has to take a slight detour.
8112 */
8113 return qulltoBasicLatin(negative ? 1u + qulonglong(-(n + 1)) : qulonglong(n), base, negative);
8114}
8115
8120{
8121#if defined(QT_CHECK_RANGE)
8122 if (base < 2 || base > 36) {
8123 qWarning("QString::setNum: Invalid base (%d)", base);
8124 base = 10;
8125 }
8126#endif
8127 return qulltoBasicLatin(n, base, false);
8128}
8129
8130
8143{
8145
8147 case 'f':
8149 break;
8150 case 'e':
8152 break;
8153 case 'g':
8155 break;
8156 default:
8157#if defined(QT_CHECK_RANGE)
8158 qWarning("QString::setNum: Invalid format char '%c'", format);
8159#endif
8160 break;
8161 }
8162
8164}
8165
8166namespace {
8167template<class ResultList, class StringSource>
8168static ResultList splitString(const StringSource &source, QStringView sep,
8169 Qt::SplitBehavior behavior, Qt::CaseSensitivity cs)
8170{
8171 ResultList list;
8172 typename StringSource::size_type start = 0;
8173 typename StringSource::size_type end;
8174 typename StringSource::size_type extra = 0;
8175 while ((end = QtPrivate::findString(QStringView(source.constData(), source.size()), start + extra, sep, cs)) != -1) {
8176 if (start != end || behavior == Qt::KeepEmptyParts)
8177 list.append(source.sliced(start, end - start));
8178 start = end + sep.size();
8179 extra = (sep.size() == 0 ? 1 : 0);
8180 }
8181 if (start != source.size() || behavior == Qt::KeepEmptyParts)
8182 list.append(source.sliced(start));
8183 return list;
8184}
8185
8186} // namespace
8187
8218QStringList QString::split(const QString &sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
8219{
8220 return splitString<QStringList>(*this, sep, behavior, cs);
8221}
8222
8227QStringList QString::split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
8228{
8229 return splitString<QStringList>(*this, QStringView(&sep, 1), behavior, cs);
8230}
8231
8249QList<QStringView> QStringView::split(QStringView sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
8250{
8251 return splitString<QList<QStringView>>(QStringView(*this), sep, behavior, cs);
8252}
8253
8254QList<QStringView> QStringView::split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
8255{
8256 return split(QStringView(&sep, 1), behavior, cs);
8257}
8258
8259#if QT_CONFIG(regularexpression)
8260namespace {
8261template<class ResultList, typename String, typename MatchingFunction>
8262static ResultList splitString(const String &source, const QRegularExpression &re,
8263 MatchingFunction matchingFunction,
8264 Qt::SplitBehavior behavior)
8265{
8266 ResultList list;
8267 if (!re.isValid()) {
8268 qtWarnAboutInvalidRegularExpression(re.pattern(), "QString::split");
8269 return list;
8270 }
8271
8272 qsizetype start = 0;
8273 qsizetype end = 0;
8275 while (iterator.hasNext()) {
8277 end = match.capturedStart();
8278 if (start != end || behavior == Qt::KeepEmptyParts)
8279 list.append(source.sliced(start, end - start));
8280 start = match.capturedEnd();
8281 }
8282
8283 if (start != source.size() || behavior == Qt::KeepEmptyParts)
8284 list.append(source.sliced(start));
8285
8286 return list;
8287}
8288} // namespace
8289
8317QStringList QString::split(const QRegularExpression &re, Qt::SplitBehavior behavior) const
8318{
8319#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
8320 const auto matchingFunction = qOverload<const QString &, qsizetype, QRegularExpression::MatchType, QRegularExpression::MatchOptions>(&QRegularExpression::globalMatch);
8321#else
8322 const auto matchingFunction = &QRegularExpression::globalMatch;
8323#endif
8324 return splitString<QStringList>(*this,
8325 re,
8326 matchingFunction,
8327 behavior);
8328}
8329
8343QList<QStringView> QStringView::split(const QRegularExpression &re, Qt::SplitBehavior behavior) const
8344{
8345 return splitString<QList<QStringView>>(*this, re, &QRegularExpression::globalMatchView, behavior);
8346}
8347
8348#endif // QT_CONFIG(regularexpression)
8349
8376{
8377 if (d.size == 0)
8378 return *this;
8379
8380 if (times <= 1) {
8381 if (times == 1)
8382 return *this;
8383 return QString();
8384 }
8385
8386 const qsizetype resultSize = times * d.size;
8387
8389 result.reserve(resultSize);
8390 if (result.capacity() != resultSize)
8391 return QString(); // not enough memory
8392
8393 memcpy(result.d.data(), d.data(), d.size * sizeof(QChar));
8394
8395 qsizetype sizeSoFar = d.size;
8396 char16_t *end = result.d.data() + sizeSoFar;
8397
8398 const qsizetype halfResultSize = resultSize >> 1;
8399 while (sizeSoFar <= halfResultSize) {
8400 memcpy(end, result.d.data(), sizeSoFar * sizeof(QChar));
8401 end += sizeSoFar;
8402 sizeSoFar <<= 1;
8403 }
8404 memcpy(end, result.d.data(), (resultSize - sizeSoFar) * sizeof(QChar));
8405 result.d.data()[resultSize] = '\0';
8406 result.d.size = resultSize;
8407 return result;
8408}
8409
8410void qt_string_normalize(QString *data, QString::NormalizationForm mode, QChar::UnicodeVersion version, qsizetype from)
8411{
8412 {
8413 // check if it's fully ASCII first, because then we have no work
8414 auto start = reinterpret_cast<const char16_t *>(data->constData());
8415 const char16_t *p = start + from;
8416 if (isAscii_helper(p, p + data->size() - from))
8417 return;
8418 if (p > start + from)
8419 from = p - start - 1; // need one before the non-ASCII to perform NFC
8420 }
8421
8422 if (version == QChar::Unicode_Unassigned) {
8423 version = QChar::currentUnicodeVersion();
8424 } else if (int(version) <= NormalizationCorrectionsVersionMax) {
8425 const QString &s = *data;
8426 QChar *d = nullptr;
8428 if (n.version > version) {
8429 qsizetype pos = from;
8430 if (QChar::requiresSurrogates(n.ucs4)) {
8431 char16_t ucs4High = QChar::highSurrogate(n.ucs4);
8432 char16_t ucs4Low = QChar::lowSurrogate(n.ucs4);
8433 char16_t oldHigh = QChar::highSurrogate(n.old_mapping);
8434 char16_t oldLow = QChar::lowSurrogate(n.old_mapping);
8435 while (pos < s.size() - 1) {
8436 if (s.at(pos).unicode() == ucs4High && s.at(pos + 1).unicode() == ucs4Low) {
8437 if (!d)
8438 d = data->data();
8439 d[pos] = QChar(oldHigh);
8440 d[++pos] = QChar(oldLow);
8441 }
8442 ++pos;
8443 }
8444 } else {
8445 while (pos < s.size()) {
8446 if (s.at(pos).unicode() == n.ucs4) {
8447 if (!d)
8448 d = data->data();
8449 d[pos] = QChar(n.old_mapping);
8450 }
8451 ++pos;
8452 }
8453 }
8454 }
8455 }
8456 }
8457
8458 if (normalizationQuickCheckHelper(data, mode, from, &from))
8459 return;
8460
8462
8463 canonicalOrderHelper(data, version, from);
8464
8466 return;
8467
8468 composeHelper(data, version, from);
8469}
8470
8475QString QString::normalized(QString::NormalizationForm mode, QChar::UnicodeVersion version) const
8476{
8477 QString copy = *this;
8478 qt_string_normalize(&copy, mode, version, 0);
8479 return copy;
8480}
8481
8482#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
8483static void checkArgEscape(QStringView s)
8484{
8485 // If we're in here, it means that qArgDigitValue has accepted the
8486 // digit. We can skip the check in case we already know it will
8487 // succeed.
8488 if (!supportUnicodeDigitValuesInArg())
8489 return;
8490
8491 const auto isNonAsciiDigit = [](QChar c) {
8492 return c.unicode() < u'0' || c.unicode() > u'9';
8493 };
8494
8495 if (std::any_of(s.begin(), s.end(), isNonAsciiDigit)) {
8496 const auto accumulateDigit = [](int partial, QChar digit) {
8497 return partial * 10 + digit.digitValue();
8498 };
8499 const int parsedNumber = std::accumulate(s.begin(), s.end(), 0, accumulateDigit);
8500
8501 qWarning("QString::arg(): the replacement \"%%%ls\" contains non-ASCII digits;\n"
8502 " it is currently being interpreted as the %d-th substitution.\n"
8503 " This is deprecated; support for non-ASCII digits will be dropped\n"
8504 " in a future version of Qt.",
8505 qUtf16Printable(s.toString()),
8506 parsedNumber);
8507 }
8508}
8509#endif
8510
8512{
8513 int min_escape; // lowest escape sequence number
8514 qsizetype occurrences; // number of occurrences of the lowest escape sequence number
8515 qsizetype locale_occurrences; // number of occurrences of the lowest escape sequence number that
8516 // contain 'L'
8517 qsizetype escape_len; // total length of escape sequences which will be replaced
8518};
8519
8521{
8522 const QChar *uc_begin = s.begin();
8523 const QChar *uc_end = s.end();
8524
8526
8527 d.min_escape = INT_MAX;
8528 d.occurrences = 0;
8529 d.escape_len = 0;
8530 d.locale_occurrences = 0;
8531
8532 const QChar *c = uc_begin;
8533 while (c != uc_end) {
8534 while (c != uc_end && c->unicode() != '%')
8535 ++c;
8536
8537 if (c == uc_end)
8538 break;
8539 const QChar *escape_start = c;
8540 if (++c == uc_end)
8541 break;
8542
8543 bool locale_arg = false;
8544 if (c->unicode() == 'L') {
8545 locale_arg = true;
8546 if (++c == uc_end)
8547 break;
8548 }
8549
8550 int escape = qArgDigitValue(*c);
8551 if (escape == -1)
8552 continue;
8553
8554 // ### Qt 7: do not allow anything but ASCII digits
8555 // in arg()'s replacements.
8556#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
8557 const QChar *escapeBegin = c;
8558 const QChar *escapeEnd = escapeBegin + 1;
8559#endif
8560
8561 ++c;
8562
8563 if (c != uc_end) {
8564 const int next_escape = qArgDigitValue(*c);
8565 if (next_escape != -1) {
8566 escape = (10 * escape) + next_escape;
8567 ++c;
8568#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
8569 ++escapeEnd;
8570#endif
8571 }
8572 }
8573
8574#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
8575 checkArgEscape(QStringView(escapeBegin, escapeEnd));
8576#endif
8577
8578 if (escape > d.min_escape)
8579 continue;
8580
8581 if (escape < d.min_escape) {
8582 d.min_escape = escape;
8583 d.occurrences = 0;
8584 d.escape_len = 0;
8585 d.locale_occurrences = 0;
8586 }
8587
8588 ++d.occurrences;
8589 if (locale_arg)
8590 ++d.locale_occurrences;
8591 d.escape_len += c - escape_start;
8592 }
8593 return d;
8594}
8595
8597 QStringView arg, QStringView larg, QChar fillChar)
8598{
8599 // Negative field-width for right-padding, positive for left-padding:
8600 const qsizetype abs_field_width = qAbs(field_width);
8601 const qsizetype result_len =
8602 s.size() - d.escape_len
8603 + (d.occurrences - d.locale_occurrences) * qMax(abs_field_width, arg.size())
8604 + d.locale_occurrences * qMax(abs_field_width, larg.size());
8605
8606 QString result(result_len, Qt::Uninitialized);
8607 QChar *rc = const_cast<QChar *>(result.unicode());
8608 QChar *const result_end = rc + result_len;
8609 qsizetype repl_cnt = 0;
8610
8611 const QChar *c = s.begin();
8612 const QChar *const uc_end = s.end();
8613 while (c != uc_end) {
8614 Q_ASSERT(d.occurrences > repl_cnt);
8615 /* We don't have to check increments of c against uc_end because, as
8616 long as d.occurrences > repl_cnt, we KNOW there are valid escape
8617 sequences remaining. */
8618
8619 const QChar *text_start = c;
8620 while (c->unicode() != '%')
8621 ++c;
8622
8623 const QChar *escape_start = c++;
8624 const bool localize = c->unicode() == 'L';
8625 if (localize)
8626 ++c;
8627
8628 int escape = qArgDigitValue(*c);
8629 if (escape != -1 && c + 1 != uc_end) {
8630 const int digit = qArgDigitValue(c[1]);
8631 if (digit != -1) {
8632 ++c;
8633 escape = 10 * escape + digit;
8634 }
8635 }
8636
8637 if (escape != d.min_escape) {
8638 memcpy(rc, text_start, (c - text_start) * sizeof(QChar));
8639 rc += c - text_start;
8640 } else {
8641 ++c;
8642
8643 memcpy(rc, text_start, (escape_start - text_start) * sizeof(QChar));
8644 rc += escape_start - text_start;
8645
8646 const QStringView use = localize ? larg : arg;
8647 const qsizetype pad_chars = abs_field_width - use.size();
8648 // (If negative, relevant loops are no-ops: no need to check.)
8649
8650 if (field_width > 0) { // left padded
8651 rc = std::fill_n(rc, pad_chars, fillChar);
8652 }
8653
8654 if (use.size())
8655 memcpy(rc, use.data(), use.size() * sizeof(QChar));
8656 rc += use.size();
8657
8658 if (field_width < 0) { // right padded
8659 rc = std::fill_n(rc, pad_chars, fillChar);
8660 }
8661
8662 if (++repl_cnt == d.occurrences) {
8663 memcpy(rc, c, (uc_end - c) * sizeof(QChar));
8664 rc += uc_end - c;
8665 Q_ASSERT(rc == result_end);
8666 c = uc_end;
8667 }
8668 }
8669 }
8670 Q_ASSERT(rc == result_end);
8671
8672 return result;
8673}
8674
8704QString QString::arg(const QString &a, int fieldWidth, QChar fillChar) const
8705{
8706 return arg(qToStringViewIgnoringNull(a), fieldWidth, fillChar);
8707}
8708
8741QString QString::arg(QStringView a, int fieldWidth, QChar fillChar) const
8742{
8744
8745 if (Q_UNLIKELY(d.occurrences == 0)) {
8746 qWarning("QString::arg: Argument missing: %ls, %ls", qUtf16Printable(*this),
8747 qUtf16Printable(a.toString()));
8748 return *this;
8749 }
8750 return replaceArgEscapes(*this, d, fieldWidth, a, a, fillChar);
8751}
8752
8777QString QString::arg(QLatin1StringView a, int fieldWidth, QChar fillChar) const
8778{
8779 QVarLengthArray<char16_t> utf16 = qt_from_latin1_to_qvla(a);
8780 return arg(QStringView(utf16.data(), utf16.size()), fieldWidth, fillChar);
8781}
8782
8870QString QString::arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
8871{
8873
8874 if (d.occurrences == 0) {
8875 qWarning() << "QString::arg: Argument missing:" << *this << ',' << a;
8876 return *this;
8877 }
8878
8879 unsigned flags = QLocaleData::NoFlags;
8880 // ZeroPadded sorts out left-padding when the fill is zero, to the right of sign:
8881 if (fillChar == u'0')
8883
8884 QString arg;
8885 if (d.occurrences > d.locale_occurrences) {
8886 arg = QLocaleData::c()->longLongToString(a, -1, base, fieldWidth, flags);
8887 Q_ASSERT(fillChar != u'0' || fieldWidth <= arg.size());
8888 }
8889
8890 QString localeArg;
8891 if (d.locale_occurrences > 0) {
8892 QLocale locale;
8893 if (!(locale.numberOptions() & QLocale::OmitGroupSeparator))
8895 localeArg = locale.d->m_data->longLongToString(a, -1, base, fieldWidth, flags);
8896 Q_ASSERT(fillChar != u'0' || fieldWidth <= localeArg.size());
8897 }
8898
8899 return replaceArgEscapes(*this, d, fieldWidth, arg, localeArg, fillChar);
8900}
8901
8916QString QString::arg(qulonglong a, int fieldWidth, int base, QChar fillChar) const
8917{
8919
8920 if (d.occurrences == 0) {
8921 qWarning() << "QString::arg: Argument missing:" << *this << ',' << a;
8922 return *this;
8923 }
8924
8925 unsigned flags = QLocaleData::NoFlags;
8926 // ZeroPadded sorts out left-padding when the fill is zero, to the right of sign:
8927 if (fillChar == u'0')
8929
8930 QString arg;
8931 if (d.occurrences > d.locale_occurrences) {
8932 arg = QLocaleData::c()->unsLongLongToString(a, -1, base, fieldWidth, flags);
8933 Q_ASSERT(fillChar != u'0' || fieldWidth <= arg.size());
8934 }
8935
8936 QString localeArg;
8937 if (d.locale_occurrences > 0) {
8938 QLocale locale;
8939 if (!(locale.numberOptions() & QLocale::OmitGroupSeparator))
8941 localeArg = locale.d->m_data->unsLongLongToString(a, -1, base, fieldWidth, flags);
8942 Q_ASSERT(fillChar != u'0' || fieldWidth <= localeArg.size());
8943 }
8944
8945 return replaceArgEscapes(*this, d, fieldWidth, arg, localeArg, fillChar);
8946}
8947
8984QString QString::arg(QChar a, int fieldWidth, QChar fillChar) const
8985{
8986 return arg(QStringView{&a, 1}, fieldWidth, fillChar);
8987}
8988
8994QString QString::arg(char a, int fieldWidth, QChar fillChar) const
8995{
8996 return arg(QLatin1Char(a), fieldWidth, fillChar);
8997}
8998
9014QString QString::arg(double a, int fieldWidth, char format, int precision, QChar fillChar) const
9015{
9017
9018 if (d.occurrences == 0) {
9019 qWarning("QString::arg: Argument missing: %s, %g", toLocal8Bit().data(), a);
9020 return *this;
9021 }
9022
9023 unsigned flags = QLocaleData::NoFlags;
9024 // ZeroPadded sorts out left-padding when the fill is zero, to the right of sign:
9025 if (fillChar == u'0')
9027
9028 if (isAsciiUpper(format))
9030
9033 case 'f':
9035 break;
9036 case 'e':
9038 break;
9039 case 'g':
9041 break;
9042 default:
9043#if defined(QT_CHECK_RANGE)
9044 qWarning("QString::arg: Invalid format char '%c'", format);
9045#endif
9046 break;
9047 }
9048
9049 QString arg;
9050 if (d.occurrences > d.locale_occurrences) {
9051 arg = QLocaleData::c()->doubleToString(a, precision, form, fieldWidth,
9053 Q_ASSERT(fillChar != u'0' || !qt_is_finite(a)
9054 || fieldWidth <= arg.size());
9055 }
9056
9057 QString localeArg;
9058 if (d.locale_occurrences > 0) {
9059 QLocale locale;
9060
9061 const QLocale::NumberOptions numberOptions = locale.numberOptions();
9062 if (!(numberOptions & QLocale::OmitGroupSeparator))
9064 if (!(numberOptions & QLocale::OmitLeadingZeroInExponent))
9066 if (numberOptions & QLocale::IncludeTrailingZeroesAfterDot)
9068 localeArg = locale.d->m_data->doubleToString(a, precision, form, fieldWidth, flags);
9069 Q_ASSERT(fillChar != u'0' || !qt_is_finite(a)
9070 || fieldWidth <= localeArg.size());
9071 }
9072
9073 return replaceArgEscapes(*this, d, fieldWidth, arg, localeArg, fillChar);
9074}
9075
9076static inline char16_t to_unicode(const QChar c) { return c.unicode(); }
9077static inline char16_t to_unicode(const char c) { return QLatin1Char{c}.unicode(); }
9078
9079template <typename Char>
9080static int getEscape(const Char *uc, qsizetype *pos, qsizetype len)
9081{
9082 qsizetype i = *pos;
9083 ++i;
9084 if (i < len && uc[i] == u'L')
9085 ++i;
9086 if (i < len) {
9087 int escape = to_unicode(uc[i]) - '0';
9088 if (uint(escape) >= 10U)
9089 return -1;
9090 ++i;
9091 if (i < len) {
9092 // there's a second digit
9093 int digit = to_unicode(uc[i]) - '0';
9094 if (uint(digit) < 10U) {
9095 escape = (escape * 10) + digit;
9096 ++i;
9097 }
9098 }
9099 *pos = i;
9100 return escape;
9101 }
9102 return -1;
9103}
9104
9105/*
9106 Algorithm for multiArg:
9107
9108 1. Parse the string as a sequence of verbatim text and placeholders (%L?\d{,3}).
9109 The L is parsed and accepted for compatibility with non-multi-arg, but since
9110 multiArg only accepts strings as replacements, the localization request can
9111 be safely ignored.
9112 2. The result of step (1) is a list of (string-ref,int)-tuples. The string-ref
9113 either points at text to be copied verbatim (in which case the int is -1),
9114 or, initially, at the textual representation of the placeholder. In that case,
9115 the int contains the numerical number as parsed from the placeholder.
9116 3. Next, collect all the non-negative ints found, sort them in ascending order and
9117 remove duplicates.
9118 3a. If the result has more entries than multiArg() was given replacement strings,
9119 we have found placeholders we can't satisfy with replacement strings. That is
9120 fine (there could be another .arg() call coming after this one), so just
9121 truncate the result to the number of actual multiArg() replacement strings.
9122 3b. If the result has less entries than multiArg() was given replacement strings,
9123 the string is missing placeholders. This is an error that the user should be
9124 warned about.
9125 4. The result of step (3) is a mapping from the index of any replacement string to
9126 placeholder number. This is the wrong way around, but since placeholder
9127 numbers could get as large as 999, while we typically don't have more than 9
9128 replacement strings, we trade 4K of sparsely-used memory for doing a reverse lookup
9129 each time we need to map a placeholder number to a replacement string index
9130 (that's a linear search; but still *much* faster than using an associative container).
9131 5. Next, for each of the tuples found in step (1), do the following:
9132 5a. If the int is negative, do nothing.
9133 5b. Otherwise, if the int is found in the result of step (3) at index I, replace
9134 the string-ref with a string-ref for the (complete) I'th replacement string.
9135 5c. Otherwise, do nothing.
9136 6. Concatenate all string refs into a single result string.
9137*/
9138
9139namespace {
9140struct Part
9141{
9142 Part() = default; // for QVarLengthArray; do not use
9143 constexpr Part(QStringView s, int num = -1)
9144 : tag{QtPrivate::ArgBase::U16}, number{num}, data{s.utf16()}, size{s.size()} {}
9145 constexpr Part(QLatin1StringView s, int num = -1)
9146 : tag{QtPrivate::ArgBase::L1}, number{num}, data{s.data()}, size{s.size()} {}
9147
9148 void reset(QStringView s) noexcept { *this = {s, number}; }
9149 void reset(QLatin1StringView s) noexcept { *this = {s, number}; }
9150
9152 int number;
9153 const void *data;
9155};
9156} // unnamed namespace
9157
9159
9160namespace {
9161
9162enum { ExpectedParts = 32 };
9163
9164typedef QVarLengthArray<Part, ExpectedParts> ParseResult;
9165typedef QVarLengthArray<int, ExpectedParts/2> ArgIndexToPlaceholderMap;
9166
9167template <typename StringView>
9168static ParseResult parseMultiArgFormatString(StringView s)
9169{
9170 ParseResult result;
9171
9172 const auto uc = s.data();
9173 const auto len = s.size();
9174 const auto end = len - 1;
9175 qsizetype i = 0;
9176 qsizetype last = 0;
9177
9178 while (i < end) {
9179 if (uc[i] == u'%') {
9180 qsizetype percent = i;
9181 int number = getEscape(uc, &i, len);
9182 if (number != -1) {
9183 if (last != percent)
9184 result.push_back(Part{s.sliced(last, percent - last)}); // literal text (incl. failed placeholders)
9185 result.push_back(Part{s.sliced(percent, i - percent), number}); // parsed placeholder
9186 last = i;
9187 continue;
9188 }
9189 }
9190 ++i;
9191 }
9192
9193 if (last < len)
9194 result.push_back(Part{s.sliced(last, len - last)}); // trailing literal text
9195
9196 return result;
9197}
9198
9199static ArgIndexToPlaceholderMap makeArgIndexToPlaceholderMap(const ParseResult &parts)
9200{
9201 ArgIndexToPlaceholderMap result;
9202
9203 for (const Part &part : parts) {
9204 if (part.number >= 0)
9205 result.push_back(part.number);
9206 }
9207
9208 std::sort(result.begin(), result.end());
9209 result.erase(std::unique(result.begin(), result.end()),
9210 result.end());
9211
9212 return result;
9213}
9214
9215static qsizetype resolveStringRefsAndReturnTotalSize(ParseResult &parts, const ArgIndexToPlaceholderMap &argIndexToPlaceholderMap, const QtPrivate::ArgBase *args[])
9216{
9217 using namespace QtPrivate;
9218 qsizetype totalSize = 0;
9219 for (Part &part : parts) {
9220 if (part.number != -1) {
9221 const auto it = std::find(argIndexToPlaceholderMap.begin(), argIndexToPlaceholderMap.end(), part.number);
9222 if (it != argIndexToPlaceholderMap.end()) {
9223 const auto &arg = *args[it - argIndexToPlaceholderMap.begin()];
9224 switch (arg.tag) {
9225 case ArgBase::L1:
9226 part.reset(static_cast<const QLatin1StringArg&>(arg).string);
9227 break;
9228 case ArgBase::U8:
9229 Q_UNREACHABLE(); // waiting for QUtf8String...
9230 break;
9231 case ArgBase::U16:
9232 part.reset(static_cast<const QStringViewArg&>(arg).string);
9233 break;
9234 }
9235 }
9236 }
9237 totalSize += part.size;
9238 }
9239 return totalSize;
9240}
9241
9242} // unnamed namespace
9243
9245Q_ALWAYS_INLINE QString to_string(QStringView s) noexcept { return s.toString(); }
9246
9247template <typename StringView>
9248static QString argToQStringImpl(StringView pattern, size_t numArgs, const QtPrivate::ArgBase **args)
9249{
9250 // Step 1-2 above
9251 ParseResult parts = parseMultiArgFormatString(pattern);
9252
9253 // 3-4
9254 ArgIndexToPlaceholderMap argIndexToPlaceholderMap = makeArgIndexToPlaceholderMap(parts);
9255
9256 if (static_cast<size_t>(argIndexToPlaceholderMap.size()) > numArgs) // 3a
9257 argIndexToPlaceholderMap.resize(qsizetype(numArgs));
9258 else if (Q_UNLIKELY(static_cast<size_t>(argIndexToPlaceholderMap.size()) < numArgs)) // 3b
9259 qWarning("QString::arg: %d argument(s) missing in %ls",
9260 int(numArgs - argIndexToPlaceholderMap.size()), qUtf16Printable(to_string(pattern)));
9261
9262 // 5
9263 const qsizetype totalSize = resolveStringRefsAndReturnTotalSize(parts, argIndexToPlaceholderMap, args);
9264
9265 // 6:
9266 QString result(totalSize, Qt::Uninitialized);
9267 auto out = const_cast<QChar*>(result.constData());
9268
9269 for (const Part &part : parts) {
9270 switch (part.tag) {
9272 if (part.size) {
9273 qt_from_latin1(reinterpret_cast<char16_t*>(out),
9274 reinterpret_cast<const char*>(part.data), part.size);
9275 }
9276 break;
9278 Q_UNREACHABLE(); // waiting for QUtf8String
9279 break;
9281 if (part.size)
9282 memcpy(out, part.data, part.size * sizeof(QChar));
9283 break;
9284 }
9285 out += part.size;
9286 }
9287
9288 return result;
9289}
9290
9295
9300
9308{
9310}
9311
9412{
9413 const auto start = std::distance(cbegin(), first);
9414 const auto len = std::distance(first, last);
9415 remove(start, len);
9416 return begin() + start;
9417}
9418
9483{
9484 return QString(DataPointer::fromRawData(const_cast<char16_t *>(reinterpret_cast<const char16_t *>(unicode)), size));
9485}
9486
9502{
9503 if (!unicode || !size) {
9504 clear();
9505 }
9506 *this = fromRawData(unicode, size);
9507 return *this;
9508}
9509
9548#if !defined(QT_NO_DATASTREAM)
9559{
9560 if (out.version() == 1) {
9561 out << str.toLatin1();
9562 } else {
9563 if (!str.isNull() || out.version() < 3) {
9565 out.writeBytes(reinterpret_cast<const char *>(str.unicode()),
9566 static_cast<qsizetype>(sizeof(QChar) * str.size()));
9567 } else {
9568 QVarLengthArray<char16_t> buffer(str.size());
9569 qbswap<sizeof(char16_t)>(str.constData(), str.size(), buffer.data());
9570 out.writeBytes(reinterpret_cast<const char *>(buffer.data()),
9571 static_cast<qsizetype>(sizeof(char16_t) * buffer.size()));
9572 }
9573 } else {
9574 QDataStream::writeQSizeType(out, -1); // write null marker
9575 }
9576 }
9577 return out;
9578}
9579
9590{
9591 if (in.version() == 1) {
9592 QByteArray l;
9593 in >> l;
9595 } else {
9596 qint64 size = QDataStream::readQSizeType(in);
9597 qsizetype bytes = size;
9598 if (size != bytes || size < -1) {
9599 str.clear();
9600 in.setStatus(QDataStream::SizeLimitExceeded);
9601 return in;
9602 }
9603 if (bytes == -1) { // null string
9604 str = QString();
9605 } else if (bytes > 0) {
9606 if (bytes & 0x1) {
9607 str.clear();
9608 in.setStatus(QDataStream::ReadCorruptData);
9609 return in;
9610 }
9611
9612 const qsizetype Step = 1024 * 1024;
9613 qsizetype len = bytes / 2;
9614 qsizetype allocated = 0;
9615
9616 while (allocated < len) {
9617 int blockSize = qMin(Step, len - allocated);
9618 str.resize(allocated + blockSize);
9619 if (in.readRawData(reinterpret_cast<char *>(str.data()) + allocated * 2,
9620 blockSize * 2) != blockSize * 2) {
9621 str.clear();
9622 in.setStatus(QDataStream::ReadPastEnd);
9623 return in;
9624 }
9625 allocated += blockSize;
9626 }
9627
9628 if ((in.byteOrder() == QDataStream::BigEndian)
9630 char16_t *data = reinterpret_cast<char16_t *>(str.data());
9631 qbswap<sizeof(*data)>(data, len, data);
9632 }
9633 } else {
9635 }
9636 }
9637 return in;
9638}
9639#endif // QT_NO_DATASTREAM
9640
9666{
9667 int isolateLevel = 0;
9668
9669 for (QStringIterator i(string); i.hasNext();) {
9670 const char32_t c = i.next();
9671
9672 switch (QChar::direction(c)) {
9673 case QChar::DirRLI:
9674 case QChar::DirLRI:
9675 case QChar::DirFSI:
9676 ++isolateLevel;
9677 break;
9678 case QChar::DirPDI:
9679 if (isolateLevel)
9680 --isolateLevel;
9681 break;
9682 case QChar::DirL:
9683 if (isolateLevel)
9684 break;
9685 return false;
9686 case QChar::DirR:
9687 case QChar::DirAL:
9688 if (isolateLevel)
9689 break;
9690 return true;
9691 case QChar::DirEN:
9692 case QChar::DirES:
9693 case QChar::DirET:
9694 case QChar::DirAN:
9695 case QChar::DirCS:
9696 case QChar::DirB:
9697 case QChar::DirS:
9698 case QChar::DirWS:
9699 case QChar::DirON:
9700 case QChar::DirLRE:
9701 case QChar::DirLRO:
9702 case QChar::DirRLE:
9703 case QChar::DirRLO:
9704 case QChar::DirPDF:
9705 case QChar::DirNSM:
9706 case QChar::DirBN:
9707 break;
9708 }
9709 }
9710 return false;
9711}
9712
9714{
9715 qsizetype num = 0;
9716 qsizetype i = -1;
9717 if (haystack.size() > 500 && needle.size() > 5) {
9718 QStringMatcher matcher(needle, cs);
9719 while ((i = matcher.indexIn(haystack, i + 1)) != -1)
9720 ++num;
9721 } else {
9722 while ((i = QtPrivate::findString(haystack, i + 1, needle, cs)) != -1)
9723 ++num;
9724 }
9725 return num;
9726}
9727
9729{
9730 if (cs == Qt::CaseSensitive)
9731 return std::count(haystack.cbegin(), haystack.cend(), needle);
9732
9733 needle = foldCase(needle);
9734 return std::count_if(haystack.cbegin(), haystack.cend(),
9735 [needle](const QChar c) { return foldAndCompare(c, needle); });
9736}
9737
9739{
9740 qsizetype num = 0;
9741 qsizetype i = -1;
9742
9743 QLatin1StringMatcher matcher(needle, cs);
9744 while ((i = matcher.indexIn(haystack, i + 1)) != -1)
9745 ++num;
9746
9747 return num;
9748}
9749
9751{
9752 if (haystack.size() < needle.size())
9753 return 0;
9754
9755 if (!QtPrivate::isLatin1(needle)) // won't find non-L1 UTF-16 needles in a L1 haystack!
9756 return 0;
9757
9758 qsizetype num = 0;
9759 qsizetype i = -1;
9760
9761 QVarLengthArray<uchar> s(needle.size());
9762 qt_to_latin1_unchecked(s.data(), needle.utf16(), needle.size());
9763
9764 QLatin1StringMatcher matcher(QLatin1StringView(reinterpret_cast<char *>(s.data()), s.size()),
9765 cs);
9766 while ((i = matcher.indexIn(haystack, i + 1)) != -1)
9767 ++num;
9768
9769 return num;
9770}
9771
9773{
9774 if (haystack.size() < needle.size())
9775 return -1;
9776
9777 QVarLengthArray<char16_t> s = qt_from_latin1_to_qvla(needle);
9778 return QtPrivate::count(haystack, QStringView(s.data(), s.size()), cs);
9779}
9780
9782{
9783 // non-L1 needles cannot possibly match in L1-only haystacks
9784 if (needle.unicode() > 0xff)
9785 return 0;
9786
9787 if (cs == Qt::CaseSensitive) {
9788 return std::count(haystack.cbegin(), haystack.cend(), needle.toLatin1());
9789 } else {
9790 return std::count_if(haystack.cbegin(), haystack.cend(),
9791 CaseInsensitiveL1::matcher(needle.toLatin1()));
9792 }
9793}
9794
9816{
9817 return qt_starts_with_impl(haystack, needle, cs);
9818}
9819
9821{
9822 return qt_starts_with_impl(haystack, needle, cs);
9823}
9824
9826{
9827 return qt_starts_with_impl(haystack, needle, cs);
9828}
9829
9831{
9832 return qt_starts_with_impl(haystack, needle, cs);
9833}
9834
9856{
9857 return qt_ends_with_impl(haystack, needle, cs);
9858}
9859
9861{
9862 return qt_ends_with_impl(haystack, needle, cs);
9863}
9864
9866{
9867 return qt_ends_with_impl(haystack, needle, cs);
9868}
9869
9871{
9872 return qt_ends_with_impl(haystack, needle, cs);
9873}
9874
9876{
9877 const qsizetype l = haystack0.size();
9878 const qsizetype sl = needle0.size();
9879 if (sl == 1)
9880 return findString(haystack0, from, needle0[0], cs);
9881 if (from < 0)
9882 from += l;
9883 if (std::size_t(sl + from) > std::size_t(l))
9884 return -1;
9885 if (!sl)
9886 return from;
9887 if (!l)
9888 return -1;
9889
9890 /*
9891 We use the Boyer-Moore algorithm in cases where the overhead
9892 for the skip table should pay off, otherwise we use a simple
9893 hash function.
9894 */
9895 if (l > 500 && sl > 5)
9896 return qFindStringBoyerMoore(haystack0, from, needle0, cs);
9897
9898 auto sv = [sl](const char16_t *v) { return QStringView(v, sl); };
9899 /*
9900 We use some hashing for efficiency's sake. Instead of
9901 comparing strings, we compare the hash value of str with that
9902 of a part of this QString. Only if that matches, we call
9903 qt_string_compare().
9904 */
9905 const char16_t *needle = needle0.utf16();
9906 const char16_t *haystack = haystack0.utf16() + from;
9907 const char16_t *end = haystack0.utf16() + (l - sl);
9908 const std::size_t sl_minus_1 = sl - 1;
9909 std::size_t hashNeedle = 0, hashHaystack = 0;
9910 qsizetype idx;
9911
9912 if (cs == Qt::CaseSensitive) {
9913 for (idx = 0; idx < sl; ++idx) {
9914 hashNeedle = ((hashNeedle<<1) + needle[idx]);
9915 hashHaystack = ((hashHaystack<<1) + haystack[idx]);
9916 }
9917 hashHaystack -= haystack[sl_minus_1];
9918
9919 while (haystack <= end) {
9920 hashHaystack += haystack[sl_minus_1];
9921 if (hashHaystack == hashNeedle
9922 && QtPrivate::compareStrings(needle0, sv(haystack), Qt::CaseSensitive) == 0)
9923 return haystack - haystack0.utf16();
9924
9925 REHASH(*haystack);
9926 ++haystack;
9927 }
9928 } else {
9929 const char16_t *haystack_start = haystack0.utf16();
9930 for (idx = 0; idx < sl; ++idx) {
9931 hashNeedle = (hashNeedle<<1) + foldCase(needle + idx, needle);
9932 hashHaystack = (hashHaystack<<1) + foldCase(haystack + idx, haystack_start);
9933 }
9934 hashHaystack -= foldCase(haystack + sl_minus_1, haystack_start);
9935
9936 while (haystack <= end) {
9937 hashHaystack += foldCase(haystack + sl_minus_1, haystack_start);
9938 if (hashHaystack == hashNeedle
9939 && QtPrivate::compareStrings(needle0, sv(haystack), Qt::CaseInsensitive) == 0)
9940 return haystack - haystack0.utf16();
9941
9942 REHASH(foldCase(haystack, haystack_start));
9943 ++haystack;
9944 }
9945 }
9946 return -1;
9947}
9948
9950{
9951 if (haystack.size() < needle.size())
9952 return -1;
9953
9954 QVarLengthArray<char16_t> s = qt_from_latin1_to_qvla(needle);
9955 return QtPrivate::findString(haystack, from, QStringView(reinterpret_cast<const QChar*>(s.constData()), s.size()), cs);
9956}
9957
9959{
9960 if (haystack.size() < needle.size())
9961 return -1;
9962
9963 if (!QtPrivate::isLatin1(needle)) // won't find non-L1 UTF-16 needles in a L1 haystack!
9964 return -1;
9965
9966 if (needle.size() == 1) {
9967 const char n = needle.front().toLatin1();
9968 return QtPrivate::findString(haystack, from, QLatin1StringView(&n, 1), cs);
9969 }
9970
9971 QVarLengthArray<char> s(needle.size());
9972 qt_to_latin1_unchecked(reinterpret_cast<uchar *>(s.data()), needle.utf16(), needle.size());
9973 return QtPrivate::findString(haystack, from, QLatin1StringView(s.data(), s.size()), cs);
9974}
9975
9977{
9978 if (from < 0)
9979 from += haystack.size();
9980 if (from < 0)
9981 return -1;
9982 qsizetype adjustedSize = haystack.size() - from;
9983 if (adjustedSize < needle.size())
9984 return -1;
9985 if (needle.size() == 0)
9986 return from;
9987
9988 if (cs == Qt::CaseSensitive) {
9989
9990 if (needle.size() == 1) {
9991 Q_ASSERT(haystack.data() != nullptr); // see size check above
9992 if (auto it = memchr(haystack.data() + from, needle.front().toLatin1(), adjustedSize))
9993 return static_cast<const char *>(it) - haystack.data();
9994 return -1;
9995 }
9996
9998 return matcher.indexIn(haystack, from);
9999 }
10000
10001 // If the needle is sufficiently small we simply iteratively search through
10002 // the haystack. When the needle is too long we use a boyer-moore searcher
10003 // from the standard library, if available. If it is not available then the
10004 // QLatin1Strings are converted to QString and compared as such. Though
10005 // initialization is slower the boyer-moore search it employs still makes up
10006 // for it when haystack and needle are sufficiently long.
10007 // The needle size was chosen by testing various lengths using the
10008 // qstringtokenizer benchmark with the
10009 // "tokenize_qlatin1string_qlatin1string" test.
10010#ifdef Q_CC_MSVC
10011 const qsizetype threshold = 1;
10012#else
10013 const qsizetype threshold = 13;
10014#endif
10015 if (needle.size() <= threshold) {
10016 const auto begin = haystack.begin();
10017 const auto end = haystack.end() - needle.size() + 1;
10018 auto ciMatch = CaseInsensitiveL1::matcher(needle[0].toLatin1());
10019 const qsizetype nlen1 = needle.size() - 1;
10020 for (auto it = std::find_if(begin + from, end, ciMatch); it != end;
10021 it = std::find_if(it + 1, end, ciMatch)) {
10022 // In this comparison we skip the first character because we know it's a match
10023 if (!nlen1 || QLatin1StringView(it + 1, nlen1).compare(needle.sliced(1), cs) == 0)
10024 return std::distance(begin, it);
10025 }
10026 return -1;
10027 }
10028
10030 return matcher.indexIn(haystack, from);
10031}
10032
10033qsizetype QtPrivate::lastIndexOf(QStringView haystack, qsizetype from, char16_t needle, Qt::CaseSensitivity cs) noexcept
10034{
10035 return qLastIndexOf(haystack, QChar(needle), from, cs);
10036}
10037
10039{
10040 return qLastIndexOf(haystack, from, needle, cs);
10041}
10042
10044{
10045 return qLastIndexOf(haystack, from, needle, cs);
10046}
10047
10049{
10050 return qLastIndexOf(haystack, from, needle, cs);
10051}
10052
10054{
10055 return qLastIndexOf(haystack, from, needle, cs);
10056}
10057
10058#if QT_CONFIG(regularexpression)
10059qsizetype QtPrivate::indexOf(QStringView viewHaystack, const QString *stringHaystack, const QRegularExpression &re, qsizetype from, QRegularExpressionMatch *rmatch)
10060{
10061 if (!re.isValid()) {
10062 qtWarnAboutInvalidRegularExpression(re.pattern(), "QString(View)::indexOf");
10063 return -1;
10064 }
10065
10066 QRegularExpressionMatch match = stringHaystack
10067 ? re.match(*stringHaystack, from)
10068 : re.matchView(viewHaystack, from);
10069 if (match.hasMatch()) {
10070 const qsizetype ret = match.capturedStart();
10071 if (rmatch)
10072 *rmatch = std::move(match);
10073 return ret;
10074 }
10075
10076 return -1;
10077}
10078
10080{
10081 return indexOf(haystack, nullptr, re, from, rmatch);
10082}
10083
10084qsizetype QtPrivate::lastIndexOf(QStringView viewHaystack, const QString *stringHaystack, const QRegularExpression &re, qsizetype from, QRegularExpressionMatch *rmatch)
10085{
10086 if (!re.isValid()) {
10087 qtWarnAboutInvalidRegularExpression(re.pattern(), "QString(View)::lastIndexOf");
10088 return -1;
10089 }
10090
10091 qsizetype endpos = (from < 0) ? (viewHaystack.size() + from + 1) : (from + 1);
10093 ? re.globalMatch(*stringHaystack)
10094 : re.globalMatchView(viewHaystack);
10095 qsizetype lastIndex = -1;
10096 while (iterator.hasNext()) {
10098 qsizetype start = match.capturedStart();
10099 if (start < endpos) {
10100 lastIndex = start;
10101 if (rmatch)
10102 *rmatch = std::move(match);
10103 } else {
10104 break;
10105 }
10106 }
10107
10108 return lastIndex;
10109}
10110
10112{
10113 return lastIndexOf(haystack, nullptr, re, from, rmatch);
10114}
10115
10116bool QtPrivate::contains(QStringView viewHaystack, const QString *stringHaystack, const QRegularExpression &re, QRegularExpressionMatch *rmatch)
10117{
10118 if (!re.isValid()) {
10119 qtWarnAboutInvalidRegularExpression(re.pattern(), "QString(View)::contains");
10120 return false;
10121 }
10122 QRegularExpressionMatch m = stringHaystack
10123 ? re.match(*stringHaystack)
10124 : re.matchView(viewHaystack);
10125 bool hasMatch = m.hasMatch();
10126 if (hasMatch && rmatch)
10127 *rmatch = std::move(m);
10128 return hasMatch;
10129}
10130
10131bool QtPrivate::contains(QStringView haystack, const QRegularExpression &re, QRegularExpressionMatch *rmatch)
10132{
10133 return contains(haystack, nullptr, re, rmatch);
10134}
10135
10137{
10138 if (!re.isValid()) {
10139 qtWarnAboutInvalidRegularExpression(re.pattern(), "QString(View)::count");
10140 return 0;
10141 }
10142 qsizetype count = 0;
10143 qsizetype index = -1;
10144 qsizetype len = haystack.size();
10145 while (index <= len - 1) {
10146 QRegularExpressionMatch match = re.matchView(haystack, index + 1);
10147 if (!match.hasMatch())
10148 break;
10149 count++;
10150
10151 // Search again, from the next character after the beginning of this
10152 // capture. If the capture starts with a surrogate pair, both together
10153 // count as "one character".
10154 index = match.capturedStart();
10155 if (index < len && haystack[index].isHighSurrogate())
10156 ++index;
10157 }
10158 return count;
10159}
10160
10161#endif // QT_CONFIG(regularexpression)
10162
10175{
10176 const auto pos = std::u16string_view(*this).find_first_of(u"<>&\"");
10177 if (pos == std::u16string_view::npos)
10178 return *this;
10179 QString rich;
10180 const qsizetype len = size();
10181 rich.reserve(qsizetype(len * 1.1));
10182 rich += qToStringViewIgnoringNull(*this).first(pos);
10183 for (auto ch : qToStringViewIgnoringNull(*this).sliced(pos)) {
10184 if (ch == u'<')
10185 rich += "&lt;"_L1;
10186 else if (ch == u'>')
10187 rich += "&gt;"_L1;
10188 else if (ch == u'&')
10189 rich += "&amp;"_L1;
10190 else if (ch == u'"')
10191 rich += "&quot;"_L1;
10192 else
10193 rich += ch;
10194 }
10195 rich.squeeze();
10196 return rich;
10197}
10198
10243#if QT_DEPRECATED_SINCE(6, 8)
10267#endif // QT_DEPRECATED_SINCE(6, 8)
10268
10298{
10299 qt_from_latin1(reinterpret_cast<char16_t *>(out), in.data(), size_t(in.size()));
10300}
10301
10387
10388#undef REHASH
\inmodule QtCore
static Q_CORE_EXPORT int compare(QAnyStringView lhs, QAnyStringView rhs, Qt::CaseSensitivity cs=Qt::CaseSensitive) noexcept
Compares the string view lhs with the string view rhs and returns a negative integer if lhs is less t...
Definition qstring.cpp:1599
static Q_CORE_EXPORT bool equal(QAnyStringView lhs, QAnyStringView rhs) noexcept
Definition qstring.cpp:1448
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:611
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
bool isNull() const noexcept
Returns true if this byte array is null; otherwise returns false.
\inmodule QtCore
static int defaultCompare(QStringView s1, QStringView s2)
\inmodule QtCore\reentrant
Definition qdatastream.h:46
constexpr QLatin1Char front() const
int compare(QStringView other, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
constexpr qsizetype size() const noexcept
void append(parameter_type t)
Definition qlist.h:458
@ OmitGroupSeparator
Definition qlocale.h:879
@ IncludeTrailingZeroesAfterDot
Definition qlocale.h:883
@ OmitLeadingZeroInExponent
Definition qlocale.h:881
\inmodule QtCore \reentrant
\inmodule QtCore \reentrant
bool hasMatch() const
Returns true if the regular expression matched against the subject string, or false otherwise.
\inmodule QtCore \reentrant
QRegularExpressionMatchIterator globalMatchView(QStringView subjectView, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
QRegularExpressionMatchIterator globalMatch(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
Attempts to perform a global match of the regular expression against the given subject string,...
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
const_iterator constBegin() const noexcept
Definition qset.h:139
\inmodule QtCore
\inmodule QtCore
bool hasNext() const
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
Q_CORE_EXPORT double toDouble(bool *ok=nullptr) const
Returns the string view converted to a double value.
Definition qstring.cpp:7909
Q_CORE_EXPORT float toFloat(bool *ok=nullptr) const
Returns the string view converted to a float value.
Definition qstring.cpp:7955
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
constexpr QStringView first(qsizetype n) const noexcept
Q_CORE_EXPORT QList< QStringView > split(QStringView sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the view into substring views wherever sep occurs, and returns the list of those string views.
Definition qstring.cpp:8249
constexpr QStringView sliced(qsizetype pos) const noexcept
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first character in the string.
Definition qstring.h:1349
QByteArray toLatin1() const &
Definition qstring.h:630
QString repeated(qsizetype times) const
Definition qstring.cpp:8375
double toDouble(bool *ok=nullptr) const
Returns the string converted to a double value.
Definition qstring.cpp:7904
void resizeForOverwrite(qsizetype size)
Definition qstring.cpp:2711
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
QString & fill(QChar c, qsizetype size=-1)
Sets every character in the string to character ch.
Definition qstring.cpp:6358
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
QString rightJustified(qsizetype width, QChar fill=u' ', bool trunc=false) const
Returns a string of size() width that contains the fill character followed by the string.
Definition qstring.cpp:7061
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
void truncate(qsizetype pos)
Truncates the string at the given position index.
Definition qstring.cpp:6319
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
static QString fromUtf16(const char16_t *, qsizetype size=-1)
Definition qstring.cpp:6045
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
static QString fromUcs4(const char32_t *, qsizetype size=-1)
Definition qstring.cpp:6077
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
const_iterator cbegin() const
Definition qstring.h:1353
QString & operator=(QChar c)
Definition qstring.cpp:2901
bool isNull() const
Returns true if this string is null; otherwise returns false.
Definition qstring.h:994
QString & setUnicode(const QChar *unicode, qsizetype size)
Resizes the string to size characters and copies unicode into the string.
Definition qstring.cpp:6100
QList< uint > toUcs4() const
Definition qstring.cpp:5823
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const_iterator cend() const
Definition qstring.h:1361
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
bool isLower() const
Returns true if the string is lowercase, that is, it's identical to its toLower() folding.
Definition qstring.cpp:5598
QString arg(qlonglong a, int fieldwidth=0, int base=10, QChar fillChar=u' ') const
Definition qstring.cpp:8870
@ SectionIncludeLeadingSep
Definition qstring.h:340
@ SectionSkipEmpty
Definition qstring.h:339
@ SectionIncludeTrailingSep
Definition qstring.h:341
void swap(QString &other) noexcept
Definition qstring.h:185
QString section(QChar sep, qsizetype start, qsizetype end=-1, SectionFlags flags=SectionDefault) const
This function returns a section of the string.
Definition qstring.h:1284
static QString fromRawData(const QChar *, qsizetype size)
Constructs a QString that uses the first size Unicode characters in the array unicode.
Definition qstring.cpp:9482
constexpr QString() noexcept
Constructs a null string.
Definition qstring.h:1322
qsizetype capacity() const
Returns the maximum number of characters that can be stored in the string without forcing a reallocat...
Definition qstring.h:1256
iterator end()
Returns an \l{STL-style iterators}{STL-style iterator} pointing just after the last character in the ...
Definition qstring.h:1357
QString leftJustified(qsizetype width, QChar fill=u' ', bool trunc=false) const
Returns a string of size width that contains this string padded by the fill character.
Definition qstring.cpp:7022
QString & assign(QAnyStringView s)
Definition qstring.cpp:3406
bool isUpper() const
Returns true if the string is uppercase, that is, it's identical to its toUpper() folding.
Definition qstring.cpp:5580
float toFloat(bool *ok=nullptr) const
Returns the string converted to a float value.
Definition qstring.cpp:7950
QChar front() const
Definition qstring.h:230
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5506
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6664
QString & insert(qsizetype i, QChar c)
Definition qstring.cpp:3132
friend qsizetype erase(QString &s, const T &t)
Definition qstring.h:1601
DataPointer & data_ptr()
Definition qstring.h:1093
QByteArray toLocal8Bit() const &
Definition qstring.h:638
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1240
qsizetype count(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4833
static QString vasprintf(const char *format, va_list ap) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7357
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1369
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
QString & append(QChar c)
Definition qstring.cpp:3252
int localeAwareCompare(const QString &s) const
Definition qstring.cpp:6907
bool isRightToLeft() const
Returns true if the string is read right to left.
Definition qstring.cpp:9307
QString & setNum(short, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.h:1257
QString & setRawData(const QChar *unicode, qsizetype size)
Definition qstring.cpp:9501
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3466
const_iterator constBegin() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first character in the st...
Definition qstring.h:1355
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
QString toHtmlEscaped() const
NormalizationForm
This enum describes the various normalized forms of Unicode text.
Definition qstring.h:617
@ NormalizationForm_KD
Definition qstring.h:620
@ NormalizationForm_C
Definition qstring.h:619
@ NormalizationForm_D
Definition qstring.h:618
const QChar * unicode() const
Returns a Unicode representation of the string.
Definition qstring.h:1230
QString normalized(NormalizationForm mode, QChar::UnicodeVersion version=QChar::Unicode_Unassigned) const
Returns the string in the given Unicode normalization mode, according to the given version of the Uni...
Definition qstring.cpp:8475
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
void resize(qsizetype size)
Sets the size of the string to size characters.
Definition qstring.cpp:2668
@ BigEndian
Definition qsysinfo.h:29
@ ByteOrder
Definition qsysinfo.h:34
\inmodule QtCore \title Classes and helpers for defining comparison operators \keyword qtcompare
Definition qcompare.h:400
QString str
[2]
a resize(100000)
b clear()
list append(new Employee("Blackpool", "Stephen"))
cache insert(employee->id(), employee)
QSet< QString >::iterator it
else opt state
[0]
short next
Definition keywords.cpp:445
typename C::iterator iterator
Combined button and popup list for selecting options.
static QString convertCase(T &str, QUnicodeTables::Case which)
Definition qstring.cpp:7158
static Q_NEVER_INLINE QString detachAndConvertCase(T &str, QStringIterator it, QUnicodeTables::Case which)
Definition qstring.cpp:7123
static Q_DECL_CONST_FUNCTION const Properties * qGetProp(char32_t ucs4) noexcept
static constexpr NormalizationCorrection uc_normalization_corrections[]
constexpr bool isAsciiDigit(char32_t c) noexcept
Definition qtools_p.h:67
constexpr bool isAsciiUpper(char32_t c) noexcept
Definition qtools_p.h:72
constexpr int qt_lencmp(qsizetype lhs, qsizetype rhs) noexcept
Definition qtools_p.h:109
constexpr char toAsciiLower(char ch) noexcept
Definition qtools_p.h:87
\macro QT_NO_KEYWORDS >
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype lastIndexOf(QByteArrayView haystack, qsizetype from, char needle) noexcept
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool endsWith(QByteArrayView haystack, QByteArrayView needle) noexcept
Q_CORE_EXPORT QString convertToQString(QAnyStringView s)
static constexpr bool q_points_into_range(const T *p, const T *b, const T *e, Cmp less={}) noexcept
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isLower(QStringView s) noexcept
Definition qstring.cpp:5557
Q_CORE_EXPORT QList< uint > convertToUcs4(QStringView str)
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isValidUtf16(QStringView s) noexcept
Definition qstring.cpp:905
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION QByteArrayView trimmed(QByteArrayView s) noexcept
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool startsWith(QByteArrayView haystack, QByteArrayView needle) noexcept
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype count(QByteArrayView haystack, QByteArrayView needle) noexcept
Q_CORE_EXPORT QByteArray convertToLocal8Bit(QStringView str)
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool equalStrings(QStringView lhs, QStringView rhs) noexcept
Definition qstring.cpp:1393
qsizetype findString(QStringView str, qsizetype from, QChar needle, Qt::CaseSensitivity cs=Qt::CaseSensitive) noexcept
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isRightToLeft(QStringView string) noexcept
Q_CORE_EXPORT QByteArray convertToLatin1(QStringView str)
Q_CORE_EXPORT QString argToQString(QStringView pattern, size_t n, const ArgBase **args)
Definition qstring.cpp:9291
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype qustrnlen(const char16_t *str, qsizetype maxlen) noexcept
Definition qstring.cpp:673
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION int compareStrings(QStringView lhs, QStringView rhs, Qt::CaseSensitivity cs=Qt::CaseSensitive) noexcept
qsizetype indexOf(const QList< V > &list, const U &u, qsizetype from) noexcept
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isAscii(QLatin1StringView s) noexcept
Definition qstring.cpp:850
Q_CORE_EXPORT QByteArray convertToUtf8(QStringView str)
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION qsizetype qustrlen(const char16_t *str) noexcept
Definition qstring.cpp:658
constexpr bool isLatin1(QLatin1StringView s) noexcept
Definition qstring.h:66
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION const char16_t * qustrcasechr(QStringView str, char16_t ch) noexcept
Definition qstring.cpp:775
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isUpper(QStringView s) noexcept
Definition qstring.cpp:5562
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION const char16_t * qustrchr(QStringView str, char16_t ch) noexcept
Definition qstring.cpp:687
constexpr Qt::strong_ordering compareThreeWay(LeftInt lhs, RightInt rhs) noexcept
CaseSensitivity
@ CaseInsensitive
@ CaseSensitive
constexpr Initialization Uninitialized
@ KeepEmptyParts
Definition qnamespace.h:127
Initialization
QString self
Definition language.cpp:58
QT_POPCOUNT_RELAXED_CONSTEXPR uint qCountLeadingZeroBits(quint32 v) noexcept
constexpr uint qCountTrailingZeroBits(quint32 v) noexcept
static jboolean copy(JNIEnv *, jobject)
const int blockSize
#define REHASH(a)
size_t qstrlen(const char *str)
size_t qstrnlen(const char *str, size_t maxlen)
void qt_to_latin1_unchecked(uchar *dst, const char16_t *uc, qsizetype len)
Definition qstring.cpp:1188
static void canonicalOrderHelper(QString *str, QChar::UnicodeVersion version, qsizetype from)
Definition qchar.cpp:1972
static auto fullConvertCase(char32_t uc, QUnicodeTables::Case which) noexcept
Definition qchar.cpp:1518
static void decomposeHelper(QString *str, bool canonical, QChar::UnicodeVersion version, qsizetype from)
Definition qchar.cpp:1812
static void composeHelper(QString *str, QChar::UnicodeVersion version, qsizetype from)
Definition qchar.cpp:1912
static bool normalizationQuickCheckHelper(QString *str, QString::NormalizationForm mode, qsizetype from, qsizetype *lastStable)
Definition qchar.cpp:2054
static char32_t foldCase(const char16_t *ch, const char16_t *start)
Definition qchar.cpp:1632
#define Q_UNLIKELY(x)
#define QT_FASTCALL
#define Q_NEVER_INLINE
#define Q_DECL_COLD_FUNCTION
#define Q_ALWAYS_INLINE
AudioChannelLayoutTag tag
bool comparesEqual(const QDir &lhs, const QDir &rhs)
Definition qdir.cpp:1819
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
QT_BEGIN_NAMESPACE Q_ALWAYS_INLINE void qToUnaligned(const T src, void *dest)
Definition qendian.h:34
NSUInteger capacity
QString qdtoBasicLatin(double d, QLocaleData::DoubleForm form, int precision, bool uppercase)
QSimpleParsedNumber< qulonglong > qstrntoull(const char *begin, qsizetype size, int base)
QSimpleParsedNumber< double > qt_asciiToDouble(const char *num, qsizetype numLen, StrayCharacterMode strayCharMode)
QString qulltoBasicLatin(qulonglong number, int base, bool negative)
#define qWarning
Definition qlogging.h:166
return ret
static ControlElement< T > * ptr(QWidget *widget)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
static Q_DECL_CONST_FUNCTION bool qt_is_finite(double d)
Definition qnumeric_p.h:117
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLenum mode
const GLfloat * m
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum src
GLenum GLuint buffer
GLint GLsizei width
GLint left
GLenum GLenum dst
GLbitfield flags
GLuint start
GLenum GLuint GLintptr offset
GLint first
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLfloat n
GLint GLsizei GLsizei GLenum format
GLsizei GLenum const void * indices
GLfloat GLfloat GLfloat GLfloat h
GLsizei GLsizei GLchar * source
GLdouble s
[6]
Definition qopenglext.h:235
GLboolean reset
GLuint res
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLuint in
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLuint GLenum option
GLenum GLsizei len
GLuint num
GLubyte * pattern
GLenum GLenum GLenum input
GLenum GLint GLint * precision
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static void split(QT_FT_Vector *b)
Q_DECL_COLD_FUNCTION void qtWarnAboutInvalidRegularExpression(const QString &pattern, const char *where)
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
static constexpr QChar sep
static const uint64_t qCompilerCpuFeatures
Definition qsimd_p.h:324
QSpan(It, EndOrSize) -> QSpan< std::remove_reference_t< q20::iter_reference_t< It > > >
SSL_CTX int void * arg
SSL_CTX int(* cb)(SSL *ssl, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
static QString argToQStringImpl(StringView pattern, size_t numArgs, const QtPrivate::ArgBase **args)
Definition qstring.cpp:9248
static bool isAscii_helper(const char16_t *&ptr, const char16_t *end)
Definition qstring.cpp:858
static Int toIntegral(QStringView string, bool *ok, int base)
Definition qstring.cpp:7638
void qt_to_latin1(uchar *dst, const char16_t *src, qsizetype length)
Definition qstring.cpp:1183
static void append_utf8(QString &qs, const char *cs, qsizetype len)
Definition qstring.cpp:7272
#define ATTRIBUTE_NO_SANITIZE
Definition qstring.cpp:366
bool qt_is_ascii(const char *&ptr, const char *end) noexcept
Definition qstring.cpp:786
static void replace_in_place(QString &str, QSpan< size_t > indices, qsizetype blen, QStringView after)
Definition qstring.cpp:3715
static bool checkCase(QStringView s, QUnicodeTables::Case c) noexcept
Definition qstring.cpp:5546
static void replace_helper(QString &str, QSpan< size_t > indices, qsizetype blen, QStringView after)
Definition qstring.cpp:3759
static int ucstrcmp(const char16_t *a, size_t alen, const Char2 *b, size_t blen)
Definition qstring.cpp:1362
static void removeStringImpl(QString &s, const T &needle, Qt::CaseSensitivity cs)
Definition qstring.cpp:3495
Q_ALWAYS_INLINE QString to_string(QLatin1StringView s) noexcept
Definition qstring.cpp:9244
static bool needsReallocate(const QString &str, qsizetype newSize)
Definition qstring.cpp:2631
static int qArgDigitValue(QChar ch) noexcept
Definition qstring.cpp:1631
#define REHASH(a)
Definition qstring.cpp:65
static void replace_with_copy(QString &str, QSpan< size_t > indices, qsizetype blen, QStringView after)
Definition qstring.cpp:3692
static int ucstrncmp(const char16_t *a, const char16_t *b, size_t l)
Definition qstring.cpp:1276
static QByteArray qt_convert_to_latin1(QStringView string)
Definition qstring.cpp:5643
static bool ucstreq(const char16_t *a, size_t alen, const Char2 *b)
Definition qstring.cpp:1351
static QList< uint > qt_convert_to_ucs4(QStringView string)
Definition qstring.cpp:5828
qsizetype qFindStringBoyerMoore(QStringView haystack, qsizetype from, QStringView needle, Qt::CaseSensitivity cs)
static QByteArray qt_convert_to_local_8bit(QStringView string)
Definition qstring.cpp:5735
static LengthMod parse_length_modifier(const char *&c) noexcept
Definition qstring.cpp:7328
static ArgEscapeData findArgEscapes(QStringView s)
Definition qstring.cpp:8520
static QByteArray qt_convert_to_utf8(QStringView str)
Definition qstring.cpp:5781
static void qt_to_latin1_internal(uchar *dst, const char16_t *src, qsizetype length)
Definition qstring.cpp:1004
LengthMod
Definition qstring.cpp:7317
@ lm_none
Definition qstring.cpp:7317
@ lm_l
Definition qstring.cpp:7317
@ lm_hh
Definition qstring.cpp:7317
@ lm_j
Definition qstring.cpp:7317
@ lm_L
Definition qstring.cpp:7317
@ lm_h
Definition qstring.cpp:7317
@ lm_z
Definition qstring.cpp:7317
@ lm_ll
Definition qstring.cpp:7317
@ lm_t
Definition qstring.cpp:7317
static void insert_helper(QString &str, qsizetype i, const T &toInsert)
Definition qstring.cpp:2970
static int latin1nicmp(const char *lhsChar, qsizetype lSize, const char *rhsChar, qsizetype rSize)
Definition qstring.cpp:1375
static QString replaceArgEscapes(QStringView s, const ArgEscapeData &d, qsizetype field_width, QStringView arg, QStringView larg, QChar fillChar)
Definition qstring.cpp:8596
static QVarLengthArray< char16_t > qt_from_latin1_to_qvla(QLatin1StringView str)
Definition qstring.cpp:995
static Q_NEVER_INLINE int ucstricmp8(const char *utf8, const char *utf8end, const QChar *utf16, const QChar *utf16end)
Definition qstring.cpp:1239
void qt_string_normalize(QString *data, QString::NormalizationForm mode, QChar::UnicodeVersion version, qsizetype from)
Definition qstring.cpp:8410
static uint parse_flag_characters(const char *&c) noexcept
Definition qstring.cpp:7280
#define CSTR_LESS_THAN
Definition qstring.cpp:6887
static Q_NEVER_INLINE int ucstricmp(qsizetype alen, const char16_t *a, qsizetype blen, const char16_t *b)
Definition qstring.cpp:1194
#define CSTR_GREATER_THAN
Definition qstring.cpp:6889
static char16_t to_unicode(const QChar c)
Definition qstring.cpp:9076
Q_CORE_EXPORT void qt_from_latin1(char16_t *dst, const char *str, size_t size) noexcept
Definition qstring.cpp:920
static int getEscape(const Char *uc, qsizetype *pos, qsizetype len)
Definition qstring.cpp:9080
static bool can_consume(const char *&c, char ch) noexcept
Definition qstring.cpp:7319
static int parse_field_width(const char *&c, qsizetype size)
Definition qstring.cpp:7300
#define qUtf16Printable(string)
Definition qstring.h:1543
static char * toLocal8Bit(char *out, QStringView in, QStringConverter::State *state)
@ DetectEndianness
QBasicUtf8StringView< false > QUtf8StringView
Definition qstringfwd.h:46
QStringView qToStringViewIgnoringNull(const QStringLike &s) noexcept
#define s2
char Char
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
@ Q_PRIMITIVE_TYPE
Definition qtypeinfo.h:157
@ Q_RELOCATABLE_TYPE
Definition qtypeinfo.h:158
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:180
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
unsigned short quint16
Definition qtypes.h:48
size_t quintptr
Definition qtypes.h:167
unsigned long ulong
Definition qtypes.h:35
ptrdiff_t qptrdiff
Definition qtypes.h:164
quint64 qulonglong
Definition qtypes.h:64
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
unsigned char quint8
Definition qtypes.h:46
qint64 qlonglong
Definition qtypes.h:63
Qt::weak_ordering compareThreeWay(const QUrl &lhs, const QUrl &rhs)
Definition qurl.cpp:3079
static const uint base
Definition qurlidna.cpp:20
static QString escape(const QString &input)
static double toDouble(Value v)
QVarLengthArray(InputIterator, InputIterator) -> QVarLengthArray< ValueType >
QT_BEGIN_NAMESPACE typedef uchar * output
QList< int > list
[14]
Q_CHECK_PTR(a=new int[80])
QByteArray ba
[0]
settings remove("monkey")
QTextStream out(stdout)
[7]
QDataStream & operator<<(QDataStream &out, const MyClass &myObj)
[4]
QDataStream & operator>>(QDataStream &in, MyClass &myObj)
QMimeDatabase db
[0]
static const auto matcher
[0]
ba fill(true)
list lastIndexOf("B")
list indexOf("B")
QSharedPointer< T > other(t)
[5]
this swap(other)
dialog exec()
QAction * at
QQuickView * view
[0]
QJSValueList args
qsizetype occurrences
Definition qstring.cpp:8514
qsizetype escape_len
Definition qstring.cpp:8517
qsizetype locale_occurrences
Definition qstring.cpp:8515
static void appendLatin1To(QLatin1StringView in, QChar *out) noexcept
void detachAndGrow(QArrayData::GrowthPosition where, qsizetype n, const T **data, QArrayDataPointer *old)
qsizetype freeSpaceAtBegin() const noexcept
bool needsDetach() const noexcept
bool isMutable() const noexcept
@ GrowsAtBeginning
Definition qarraydata.h:33
\inmodule QtCore \reentrant
Definition qchar.h:18
constexpr char16_t unicode() const noexcept
Converts a Latin-1 character to an 16-bit-encoded Unicode representation of the character.
Definition qchar.h:22
static char16_t * convertToUnicode(char16_t *dst, QLatin1StringView in) noexcept
Definition qstring.cpp:5687
static char * convertFromUnicode(char *out, QStringView in, QStringConverter::State *state) noexcept
static float convertDoubleToFloat(double d, bool *ok)
Definition qlocale_p.h:309
static const QLocaleData * c()
Definition qlocale.cpp:856
static QSimpleParsedNumber< quint64 > bytearrayToUnsLongLong(QByteArrayView num, int base)
Definition qlocale.cpp:4524
@ BlankBeforePositive
Definition qlocale_p.h:262
@ AddTrailingZeroes
Definition qlocale_p.h:259
@ DFSignificantDigits
Definition qlocale_p.h:253
static Q_CORE_EXPORT QSimpleParsedNumber< qint64 > bytearrayToLongLong(QByteArrayView num, int base)
Definition qlocale.cpp:4516
static TrimPositions trimmed_helper_positions(const StringType &str)
static StringType trimmed_helper(StringType &str)
static StringType simplified_helper(StringType &str)
static QChar * convertToUnicode(QChar *out, QByteArrayView, QStringConverter::State *state, DataEndianness endian)
static Q_CORE_EXPORT QByteArray convertFromUnicode(QStringView in)
static int compareUtf8(QByteArrayView utf8, QStringView utf16, Qt::CaseSensitivity cs=Qt::CaseSensitive) noexcept
static QChar * convertToUnicode(QChar *buffer, QByteArrayView in) noexcept
static int difference(char lhs, char rhs)