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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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