1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#ifndef QLOCALE_P_H
7#define QLOCALE_P_H
8
9//
10// W A R N I N G
11// -------------
12//
13// This file is not part of the Qt API. It exists for the convenience
14// of internal files. This header file may change from version to version
15// without notice, or even be removed.
16//
17// We mean it.
18//
19
20#include "qlocale.h"
21
22#include <QtCore/qcalendar.h>
23#include <QtCore/qlist.h>
24#include <QtCore/qnumeric.h>
25#include <QtCore/private/qnumeric_p.h>
26#include <QtCore/qstring.h>
27#include <QtCore/qvariant.h>
28#include <QtCore/qvarlengtharray.h>
29#ifdef Q_OS_WASM
30#include <private/qstdweb_p.h>
31#endif
32
33#include <limits>
34#include <cmath>
35#include <string_view>
36
37QT_BEGIN_NAMESPACE
38
39template <typename T> struct QSimpleParsedNumber
40{
41 T result;
42 // When used < 0, -used is how much was used, but it was an error.
43 qsizetype used;
44 bool ok() const { return used > 0; }
45};
46
47template <int Extent, uchar Lowest> struct QCharacterSetMatch
48{
49 using Word = qregisteruint;
50 static constexpr int WordBits = std::numeric_limits<Word>::digits;
51 static constexpr int MaxRange = WordBits * Extent;
52 qregisteruint mask[Extent];
53
54 constexpr QCharacterSetMatch(std::string_view set) noexcept
55 : mask{}
56 {
57 for (char c : set) {
58 auto [offset, shift] = maskLocation(c);
59 mask[offset] |= Word(1) << shift;
60 }
61 }
62
63 constexpr bool matches(uchar c) const noexcept
64 {
65 auto [offset, shift] = maskLocation(c);
66 if (offset < 0)
67 return false;
68 Word m = 0;
69 if constexpr (Extent == 2) {
70 // special case for faster code (with GCC, at least)
71 m = (c - Lowest < WordBits) ? mask[0] : mask[1];
72 } else {
73 m = mask[offset];
74 }
75 return (m >> shift) & 1;
76 }
77
78 constexpr auto maskLocation(uchar c) const noexcept
79 {
80 struct { int offset; int shift; } r = { -1, -1 };
81 unsigned idx = c - Lowest;
82 if (idx < MaxRange) {
83 r.offset = idx / WordBits;
84 r.shift = idx % WordBits;
85 }
86 return r;
87 }
88};
89
90namespace QtPrivate {
91inline constexpr char ascii_space_chars[] =
92 "\t" // 9: HT - horizontal tab
93 "\n" // 10: LF - line feed
94 "\v" // 11: VT - vertical tab
95 "\f" // 12: FF - form feed
96 "\r" // 13: CR - carriage return
97 " "; // 32: space
98
99template <const char *Set, int ForcedLowest = -1>
100inline constexpr auto makeCharacterSetMatch() noexcept
101{
102 constexpr int BitsPerWord = std::numeric_limits<qregisteruint>::digits;
103 constexpr auto view = std::string_view(Set);
104 constexpr uchar MinElement = *std::min_element(first: view.begin(), last: view.end());
105 constexpr uchar MaxElement = *std::max_element(first: view.begin(), last: view.end());
106 constexpr int Range = MaxElement - MinElement;
107 constexpr int Extent = (Range + BitsPerWord - 1) / BitsPerWord;
108 constexpr int TotalBits = BitsPerWord * Extent;
109
110 if constexpr (ForcedLowest >= 0) {
111 // use the force
112 static_assert(ForcedLowest <= int(MinElement), "The force is not with you");
113 static_assert(ForcedLowest + TotalBits >= MaxElement, "The force is not with you");
114 return QCharacterSetMatch<Extent, ForcedLowest>(view);
115 } else if constexpr (MaxElement < TotalBits) {
116 // if we can use a Lowest of zero, we can remove a subtraction
117 // from the matches() code at runtime
118 return QCharacterSetMatch<Extent, 0>(view);
119 } else {
120 return QCharacterSetMatch<Extent, MinElement>(view);
121 }
122}
123} // QtPrivate
124
125// Subclassed by Android platform plugin:
126class Q_CORE_EXPORT QSystemLocale
127{
128 Q_DISABLE_COPY_MOVE(QSystemLocale)
129 QSystemLocale *next = nullptr; // Maintains a stack.
130
131public:
132 QSystemLocale();
133 virtual ~QSystemLocale();
134
135 struct CurrencyToStringArgument
136 {
137 CurrencyToStringArgument() { }
138 CurrencyToStringArgument(const QVariant &v, const QString &s)
139 : value(v), symbol(s) { }
140 QVariant value;
141 QString symbol;
142 };
143
144 enum QueryType {
145 LanguageId, // uint
146 TerritoryId, // uint
147 DecimalPoint, // QString
148 Grouping, // QLocaleData::GroupSizes
149 GroupSeparator, // QString (empty QString means: don't group digits)
150 ZeroDigit, // QString
151 NegativeSign, // QString
152 DateFormatLong, // QString
153 DateFormatShort, // QString
154 TimeFormatLong, // QString
155 TimeFormatShort, // QString
156 DayNameLong, // QString, in: int
157 DayNameShort, // QString, in: int
158 DayNameNarrow, // QString, in: int
159 MonthNameLong, // QString, in: int
160 MonthNameShort, // QString, in: int
161 MonthNameNarrow, // QString, in: int
162 DateToStringLong, // QString, in: QDate
163 DateToStringShort, // QString in: QDate
164 TimeToStringLong, // QString in: QTime
165 TimeToStringShort, // QString in: QTime
166 DateTimeFormatLong, // QString
167 DateTimeFormatShort, // QString
168 DateTimeToStringLong, // QString in: QDateTime
169 DateTimeToStringShort, // QString in: QDateTime
170 MeasurementSystem, // uint
171 PositiveSign, // QString
172 AMText, // QString
173 PMText, // QString
174 FirstDayOfWeek, // Qt::DayOfWeek
175 Weekdays, // QList<Qt::DayOfWeek>
176 CurrencySymbol, // QString in: CurrencyToStringArgument
177 CurrencyToString, // QString in: qlonglong, qulonglong or double
178 Collation, // QString
179 UILanguages, // QStringList
180 StringToStandardQuotation, // QString in: QStringView to quote
181 StringToAlternateQuotation, // QString in: QStringView to quote
182 ScriptId, // uint
183 ListToSeparatedString, // QString
184 LocaleChanged, // system locale changed
185 NativeLanguageName, // QString
186 NativeTerritoryName, // QString
187 StandaloneMonthNameLong, // QString, in: int
188 StandaloneMonthNameShort, // QString, in: int
189 StandaloneMonthNameNarrow, // QString, in: int
190 StandaloneDayNameLong, // QString, in: int
191 StandaloneDayNameShort, // QString, in: int
192 StandaloneDayNameNarrow // QString, in: int
193 };
194 virtual QVariant query(QueryType type, QVariant &&in = QVariant()) const;
195
196 virtual QLocale fallbackLocale() const;
197 inline qsizetype fallbackLocaleIndex() const;
198
199protected:
200 inline const QSharedDataPointer<QLocalePrivate> localeData(const QLocale &locale) const
201 {
202 return locale.d;
203 }
204};
205Q_DECLARE_TYPEINFO(QSystemLocale::QueryType, Q_PRIMITIVE_TYPE);
206Q_DECLARE_TYPEINFO(QSystemLocale::CurrencyToStringArgument, Q_RELOCATABLE_TYPE);
207
208struct QLocaleId
209{
210 [[nodiscard]] Q_AUTOTEST_EXPORT static QLocaleId fromName(QStringView name) noexcept;
211 [[nodiscard]] inline bool operator==(QLocaleId other) const noexcept
212 { return language_id == other.language_id && script_id == other.script_id && territory_id == other.territory_id; }
213 [[nodiscard]] inline bool operator!=(QLocaleId other) const noexcept
214 { return !operator==(other); }
215 [[nodiscard]] inline bool isValid() const noexcept
216 {
217 return language_id <= QLocale::LastLanguage && script_id <= QLocale::LastScript
218 && territory_id <= QLocale::LastTerritory;
219 }
220 [[nodiscard]] inline bool matchesAll() const noexcept
221 {
222 return !language_id && !script_id && !territory_id;
223 }
224 // Use as: filter.accept...(candidate)
225 [[nodiscard]] inline bool acceptLanguage(quint16 lang) const noexcept
226 {
227 // Always reject AnyLanguage (only used for last entry in locale_data array).
228 // So, when searching for AnyLanguage, accept everything *but* AnyLanguage.
229 return language_id ? lang == language_id : lang;
230 }
231 [[nodiscard]] inline bool acceptScriptTerritory(QLocaleId other) const noexcept
232 {
233 return (!territory_id || other.territory_id == territory_id)
234 && (!script_id || other.script_id == script_id);
235 }
236
237 [[nodiscard]] QLocaleId withLikelySubtagsAdded() const noexcept;
238 [[nodiscard]] QLocaleId withLikelySubtagsRemoved() const noexcept;
239
240 [[nodiscard]] QByteArray name(char separator = '-') const;
241
242 ushort language_id = 0, script_id = 0, territory_id = 0;
243};
244Q_DECLARE_TYPEINFO(QLocaleId, Q_PRIMITIVE_TYPE);
245
246
247using CharBuff = QVarLengthArray<char, 256>;
248
249struct QLocaleData
250{
251public:
252 // Having an index for each locale enables us to have diverse sources of
253 // data, e.g. calendar locales, as well as the main CLDR-derived data.
254 [[nodiscard]] static qsizetype findLocaleIndex(QLocaleId localeId) noexcept;
255 [[nodiscard]] static const QLocaleData *c() noexcept;
256 [[nodiscard]] Q_AUTOTEST_EXPORT
257 static bool allLocaleDataRows(bool (*check)(qsizetype, const QLocaleData &));
258
259 enum DoubleForm {
260 DFExponent = 0,
261 DFDecimal,
262 DFSignificantDigits,
263 _DFMax = DFSignificantDigits
264 };
265
266 enum Flags {
267 NoFlags = 0,
268 AddTrailingZeroes = 0x01,
269 ZeroPadded = 0x02,
270 LeftAdjusted = 0x04,
271 BlankBeforePositive = 0x08,
272 AlwaysShowSign = 0x10,
273 GroupDigits = 0x20,
274 CapitalEorX = 0x40,
275
276 ShowBase = 0x80,
277 UppercaseBase = 0x100,
278 ZeroPadExponent = 0x200,
279 ForcePoint = 0x400
280 };
281
282 enum NumberMode { IntegerMode, DoubleStandardMode, DoubleScientificMode };
283
284 struct GroupSizes // Numbers of digits in various groups:
285 {
286 int first = 0; // Min needed before the separator, when there's only one.
287 int higher = 0; // Each group between separators.
288 int least = 0; // Least significant, when any separators appear.
289 bool isValid() const { return least > 0 && higher > first && first > 0; }
290 };
291
292 struct ParsingResult
293 {
294 enum State { // A duplicate of QValidator::State
295 Invalid,
296 Intermediate,
297 Acceptable,
298 };
299
300 State state = Invalid;
301 CharBuff buff;
302 };
303
304private:
305 enum PrecisionMode {
306 PMDecimalDigits = 0x01,
307 PMSignificantDigits = 0x02,
308 PMChopTrailingZeros = 0x03
309 };
310
311 [[nodiscard]] QString decimalForm(QString &&digits, int decpt, int precision,
312 PrecisionMode pm, bool mustMarkDecimal,
313 bool groupDigits) const;
314 [[nodiscard]] QString exponentForm(QString &&digits, int decpt, int precision,
315 PrecisionMode pm, bool mustMarkDecimal,
316 int minExponentDigits) const;
317 [[nodiscard]] QString signPrefix(bool negative, unsigned flags) const;
318 [[nodiscard]] QString applyIntegerFormatting(QString &&numStr, bool negative, int precision,
319 int base, int width, unsigned flags) const;
320
321public:
322 [[nodiscard]] QString doubleToString(double d,
323 int precision = -1,
324 DoubleForm form = DFSignificantDigits,
325 int width = -1,
326 unsigned flags = NoFlags) const;
327 [[nodiscard]] QString longLongToString(qint64 l, int precision = -1,
328 int base = 10,
329 int width = -1,
330 unsigned flags = NoFlags) const;
331 [[nodiscard]] QString unsLongLongToString(quint64 l, int precision = -1,
332 int base = 10,
333 int width = -1,
334 unsigned flags = NoFlags) const;
335
336 // this function is meant to be called with the result of stringToDouble or bytearrayToDouble
337 // so *ok must have been properly set (if not null)
338 [[nodiscard]] static float convertDoubleToFloat(double d, bool *ok)
339 {
340 float result;
341 bool b = convertDoubleTo<float>(v: d, value: &result);
342 if (ok && *ok)
343 *ok = b;
344 return result;
345 }
346
347 [[nodiscard]] double stringToDouble(QStringView str, bool *ok,
348 QLocale::NumberOptions options) const;
349 [[nodiscard]] QSimpleParsedNumber<qint64>
350 stringToLongLong(QStringView str, int base, QLocale::NumberOptions options) const;
351 [[nodiscard]] QSimpleParsedNumber<quint64>
352 stringToUnsLongLong(QStringView str, int base, QLocale::NumberOptions options) const;
353
354 // this function is used in QIntValidator (QtGui)
355 [[nodiscard]] Q_CORE_EXPORT
356 static QSimpleParsedNumber<qint64> bytearrayToLongLong(QByteArrayView num, int base);
357 [[nodiscard]] static QSimpleParsedNumber<quint64>
358 bytearrayToUnsLongLong(QByteArrayView num, int base);
359
360 [[nodiscard]] bool numberToCLocale(QStringView s, QLocale::NumberOptions number_options,
361 NumberMode mode, CharBuff *result) const;
362
363 struct NumericData
364 {
365#ifndef QT_NO_SYSTEMLOCALE
366 // Only used for the system locale, to store data for the view to look at:
367 QString sysDecimal, sysGroup, sysMinus, sysPlus;
368#endif
369 QStringView decimal, group, minus, plus, exponent;
370 char32_t zeroUcs = 0;
371 qint8 zeroLen = 0;
372 bool isC = false; // C locale sets this and nothing else.
373 bool exponentCyrillic = false; // True only for floating-point parsing of Cyrillic.
374 void setZero(QStringView zero)
375 {
376 // No known locale has digits that are more than one Unicode
377 // code-point, so we can safely deal with digits as plain char32_t.
378 switch (zero.size()) {
379 case 1:
380 Q_ASSERT(!zero.at(0).isSurrogate());
381 zeroUcs = zero.at(n: 0).unicode();
382 zeroLen = 1;
383 break;
384 case 2:
385 Q_ASSERT(zero.at(0).isHighSurrogate());
386 Q_ASSERT(zero.at(1).isLowSurrogate());
387 zeroUcs = QChar::surrogateToUcs4(high: zero.at(n: 0), low: zero.at(n: 1));
388 zeroLen = 2;
389 break;
390 default:
391 Q_ASSERT(zero.size() == 0); // i.e. we got no value to use
392 break;
393 }
394 }
395 [[nodiscard]] bool isValid(NumberMode mode) const // Asserted as a sanity check.
396 {
397 if (isC)
398 return true;
399 if (exponentCyrillic && exponent != u"E" && exponent != u"\u0415")
400 return false;
401 return (zeroLen == 1 || zeroLen == 2) && zeroUcs > 0
402 && (mode == IntegerMode || !decimal.isEmpty())
403 // group may be empty (user config in system locale)
404 && !minus.isEmpty() && !plus.isEmpty()
405 && (mode != DoubleScientificMode || !exponent.isEmpty());
406 }
407 };
408 [[nodiscard]] inline NumericData numericData(NumberMode mode) const;
409
410 // this function is used in QIntValidator (QtGui)
411 [[nodiscard]] Q_CORE_EXPORT ParsingResult
412 validateChars(QStringView str, NumberMode numMode, int decDigits = -1,
413 QLocale::NumberOptions number_options = QLocale::DefaultNumberOptions) const;
414
415 // Access to assorted data members:
416 [[nodiscard]] QLocaleId id() const
417 { return QLocaleId { .language_id: m_language_id, .script_id: m_script_id, .territory_id: m_territory_id }; }
418
419 [[nodiscard]] QString decimalPoint() const;
420 [[nodiscard]] QString groupSeparator() const;
421 [[nodiscard]] QString listSeparator() const;
422 [[nodiscard]] QString percentSign() const;
423 [[nodiscard]] QString zeroDigit() const;
424 [[nodiscard]] char32_t zeroUcs() const;
425 [[nodiscard]] QString positiveSign() const;
426 [[nodiscard]] QString negativeSign() const;
427 [[nodiscard]] QString exponentSeparator() const;
428 [[nodiscard]] Q_CORE_EXPORT GroupSizes groupSizes() const;
429
430 struct DataRange
431 {
432 using Index = quint32;
433 Index offset; // Some zone data tables are big.
434 Index size; // (for consistency and to avoid struct-padding)
435 [[nodiscard]] QString getData(const char16_t *table) const
436 {
437 return size > 0
438 ? QString::fromRawData(stringStart(table), size: stringSize())
439 : QString();
440 }
441 [[nodiscard]] QStringView viewData(const char16_t *table) const
442 {
443 return { stringStart(table), stringSize() };
444 }
445 [[nodiscard]] QString getListEntry(const char16_t *table, qsizetype index) const
446 {
447 return listEntry(table, index).getData(table);
448 }
449 [[nodiscard]] QStringView viewListEntry(const char16_t *table, qsizetype index) const
450 {
451 return listEntry(table, index).viewData(table);
452 }
453 [[nodiscard]] char32_t ucsFirst(const char16_t *table) const
454 {
455 if (size && !QChar::isSurrogate(ucs4: table[offset]))
456 return table[offset];
457 if (size > 1 && QChar::isHighSurrogate(ucs4: table[offset]))
458 return QChar::surrogateToUcs4(high: table[offset], low: table[offset + 1]);
459 return 0;
460 }
461 private:
462 [[nodiscard]] const QChar *stringStart(const char16_t *table) const
463 {
464 return reinterpret_cast<const QChar *>(table + offset);
465 }
466 [[nodiscard]] qsizetype stringSize() const
467 {
468 // On 32-bit platforms, this is a narrowing cast, but the size has
469 // always come from an 8-bit or 16-bit table value so can't actually
470 // have a problem with that.
471 qsizetype result = static_cast<qsizetype>(size);
472 Q_ASSERT(result >= 0);
473 return result;
474 }
475 [[nodiscard]] DataRange listEntry(const char16_t *table, qsizetype index) const
476 {
477 const char16_t separator = ';';
478 Index i = 0;
479 while (index > 0 && i < size) {
480 if (table[offset + i] == separator)
481 index--;
482 i++;
483 }
484 Index end = i;
485 while (end < size && table[offset + end] != separator)
486 end++;
487 return { .offset: offset + i, .size: end - i };
488 }
489 };
490
491#define ForEachQLocaleRange(X) \
492 X(startListPattern) X(midListPattern) X(endListPattern) X(pairListPattern) X(listDelimit) \
493 X(decimalSeparator) X(groupDelim) X(percent) X(zero) X(minus) X(plus) X(exponential) \
494 X(quoteStart) X(quoteEnd) X(quoteStartAlternate) X(quoteEndAlternate) \
495 X(longDateFormat) X(shortDateFormat) X(longTimeFormat) X(shortTimeFormat) \
496 X(longDayNamesStandalone) X(longDayNames) \
497 X(shortDayNamesStandalone) X(shortDayNames) \
498 X(narrowDayNamesStandalone) X(narrowDayNames) \
499 X(anteMeridiem) X(postMeridiem) \
500 X(byteCount) X(byteAmountSI) X(byteAmountIEC) \
501 X(currencySymbol) X(currencyDisplayName) \
502 X(currencyFormat) X(currencyFormatNegative) \
503 X(endonymLanguage) X(endonymTerritory)
504
505#define rangeGetter(name) \
506 [[nodiscard]] DataRange name() const { return { m_ ## name ## _idx, m_ ## name ## _size }; }
507 ForEachQLocaleRange(rangeGetter)
508#undef rangeGetter
509
510public:
511 quint16 m_language_id, m_script_id, m_territory_id;
512
513 // Offsets, then sizes, for each range:
514#define rangeIndex(name) quint16 m_ ## name ## _idx;
515 ForEachQLocaleRange(rangeIndex)
516#undef rangeIndex
517#define Size(name) quint8 m_ ## name ## _size;
518 ForEachQLocaleRange(Size)
519#undef Size
520
521#undef ForEachQLocaleRange
522
523 // Strays:
524 char m_currency_iso_code[3];
525 quint8 m_currency_digits : 2;
526 quint8 m_currency_rounding : 3; // (not yet used !)
527 quint8 m_first_day_of_week : 3;
528 quint8 m_weekend_start : 3;
529 quint8 m_weekend_end : 3;
530 quint8 m_grouping_first : 2; // Don't group until more significant group has this many digits.
531 quint8 m_grouping_higher : 3; // Number of digits between grouping separators
532 quint8 m_grouping_least : 3; // Number of digits after last grouping separator (before decimal).
533};
534
535Q_DECLARE_TYPEINFO(QLocaleData::GroupSizes, Q_PRIMITIVE_TYPE);
536
537class QLocalePrivate
538{
539public:
540 constexpr QLocalePrivate(const QLocaleData *data, qsizetype index,
541 QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions,
542 int refs = 0)
543 : m_data(data), ref Q_BASIC_ATOMIC_INITIALIZER(refs),
544 m_index(index), m_numberOptions(numberOptions) {}
545
546 [[nodiscard]] quint16 languageId() const { return m_data->m_language_id; }
547 [[nodiscard]] quint16 territoryId() const { return m_data->m_territory_id; }
548
549 [[nodiscard]] QByteArray bcp47Name(char separator = '-') const;
550
551 [[nodiscard]] inline std::array<char, 4>
552 languageCode(QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode) const
553 {
554 return languageToCode(language: QLocale::Language(m_data->m_language_id), codeTypes);
555 }
556 [[nodiscard]] inline QLatin1StringView scriptCode() const
557 { return scriptToCode(script: QLocale::Script(m_data->m_script_id)); }
558 [[nodiscard]] inline QLatin1StringView territoryCode() const
559 { return territoryToCode(territory: QLocale::Territory(m_data->m_territory_id)); }
560
561 [[nodiscard]] static const QLocalePrivate *get(const QLocale &l) { return l.d; }
562 [[nodiscard]] static std::array<char, 4>
563 languageToCode(QLocale::Language language,
564 QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode);
565 [[nodiscard]] static QLatin1StringView scriptToCode(QLocale::Script script);
566 [[nodiscard]] static QLatin1StringView territoryToCode(QLocale::Territory territory);
567 [[nodiscard]] static QLocale::Language
568 codeToLanguage(QStringView code,
569 QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode) noexcept;
570 [[nodiscard]] static QLocale::Script codeToScript(QStringView code) noexcept;
571 [[nodiscard]] static QLocale::Territory codeToTerritory(QStringView code) noexcept;
572
573 [[nodiscard]] QLocale::MeasurementSystem measurementSystem() const;
574
575 [[nodiscard]] QString toUpper(const QString &str, bool *ok) const;
576 [[nodiscard]] QString toLower(const QString &str, bool *ok) const;
577
578 // System locale has an m_data all its own; all others have m_data = locale_data + m_index
579 const QLocaleData *const m_data;
580 QBasicAtomicInt ref;
581 qsizetype m_index; // System locale needs this updated when m_data->id() changes.
582 QLocale::NumberOptions m_numberOptions;
583
584 static QBasicAtomicInt s_generation;
585};
586
587#ifndef QT_NO_SYSTEMLOCALE
588qsizetype QSystemLocale::fallbackLocaleIndex() const { return fallbackLocale().d->m_index; }
589#endif
590
591template <>
592inline QLocalePrivate *QSharedDataPointer<QLocalePrivate>::clone()
593{
594 // cannot use QLocalePrivate's copy constructor
595 // since it is deleted in C++11
596 return new QLocalePrivate(d->m_data, d->m_index, d->m_numberOptions);
597}
598
599// Also used to merely skip over an escape in a format string, advancint idx to
600// point after it (so not [[nodiscard]]):
601QString qt_readEscapedFormatString(QStringView format, qsizetype *idx);
602[[nodiscard]] bool qt_splitLocaleName(QStringView name, QStringView *lang = nullptr,
603 QStringView *script = nullptr,
604 QStringView *cntry = nullptr) noexcept;
605[[nodiscard]] qsizetype qt_repeatCount(QStringView s) noexcept;
606
607[[nodiscard]] constexpr inline bool ascii_isspace(uchar c) noexcept
608{
609 constexpr auto matcher = QtPrivate::makeCharacterSetMatch<QtPrivate::ascii_space_chars>();
610 return matcher.matches(c);
611}
612
613QT_END_NAMESPACE
614
615// ### move to qnamespace.h
616QT_DECL_METATYPE_EXTERN_TAGGED(QList<Qt::DayOfWeek>, QList_Qt__DayOfWeek, Q_CORE_EXPORT)
617#ifndef QT_NO_SYSTEMLOCALE
618QT_DECL_METATYPE_EXTERN_TAGGED(QSystemLocale::CurrencyToStringArgument,
619 QSystemLocale__CurrencyToStringArgument, Q_CORE_EXPORT)
620#endif
621
622#endif // QLOCALE_P_H
623

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