1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Copyright (C) 2016 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtCore module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#ifndef QLOCALE_P_H
42#define QLOCALE_P_H
43
44//
45// W A R N I N G
46// -------------
47//
48// This file is not part of the Qt API. It exists for the convenience
49// of internal files. This header file may change from version to version
50// without notice, or even be removed.
51//
52// We mean it.
53//
54
55#include <QtCore/private/qglobal_p.h>
56#include "QtCore/qstring.h"
57#include "QtCore/qvarlengtharray.h"
58#include "QtCore/qvariant.h"
59#include "QtCore/qnumeric.h"
60#include <QtCore/qcalendar.h>
61
62#include "qlocale.h"
63
64#include <limits>
65#include <cmath>
66
67QT_BEGIN_NAMESPACE
68
69#ifndef QT_NO_SYSTEMLOCALE
70struct QLocaleData;
71class Q_CORE_EXPORT QSystemLocale
72{
73public:
74 QSystemLocale();
75 virtual ~QSystemLocale();
76
77 struct CurrencyToStringArgument
78 {
79 CurrencyToStringArgument() { }
80 CurrencyToStringArgument(const QVariant &v, const QString &s)
81 : value(v), symbol(s) { }
82 QVariant value;
83 QString symbol;
84 };
85
86 enum QueryType {
87 LanguageId, // uint
88 CountryId, // uint
89 DecimalPoint, // QString
90 GroupSeparator, // QString (empty QString means: don't group digits)
91 ZeroDigit, // QString
92 NegativeSign, // QString
93 DateFormatLong, // QString
94 DateFormatShort, // QString
95 TimeFormatLong, // QString
96 TimeFormatShort, // QString
97 DayNameLong, // QString, in: int
98 DayNameShort, // QString, in: int
99 MonthNameLong, // QString, in: int
100 MonthNameShort, // QString, in: int
101 DateToStringLong, // QString, in: QDate
102 DateToStringShort, // QString in: QDate
103 TimeToStringLong, // QString in: QTime
104 TimeToStringShort, // QString in: QTime
105 DateTimeFormatLong, // QString
106 DateTimeFormatShort, // QString
107 DateTimeToStringLong, // QString in: QDateTime
108 DateTimeToStringShort, // QString in: QDateTime
109 MeasurementSystem, // uint
110 PositiveSign, // QString
111 AMText, // QString
112 PMText, // QString
113 FirstDayOfWeek, // Qt::DayOfWeek
114 Weekdays, // QList<Qt::DayOfWeek>
115 CurrencySymbol, // QString in: CurrencyToStringArgument
116 CurrencyToString, // QString in: qlonglong, qulonglong or double
117 Collation, // QString
118 UILanguages, // QStringList
119 StringToStandardQuotation, // QString in: QStringRef to quote
120 StringToAlternateQuotation, // QString in: QStringRef to quote
121 ScriptId, // uint
122 ListToSeparatedString, // QString
123 LocaleChanged, // system locale changed
124 NativeLanguageName, // QString
125 NativeCountryName, // QString
126 StandaloneMonthNameLong, // QString, in: int
127 StandaloneMonthNameShort // QString, in: int
128 };
129 virtual QVariant query(QueryType type, QVariant in) const;
130 virtual QLocale fallbackUiLocale() const;
131
132 inline const QLocaleData *fallbackUiLocaleData() const;
133private:
134 QSystemLocale(bool);
135 friend class QSystemLocaleSingleton;
136};
137Q_DECLARE_TYPEINFO(QSystemLocale::QueryType, Q_PRIMITIVE_TYPE);
138Q_DECLARE_TYPEINFO(QSystemLocale::CurrencyToStringArgument, Q_MOVABLE_TYPE);
139#endif
140
141#if QT_CONFIG(icu)
142namespace QIcu {
143 QString toUpper(const QByteArray &localeId, const QString &str, bool *ok);
144 QString toLower(const QByteArray &localeId, const QString &str, bool *ok);
145}
146#endif
147
148
149struct QLocaleId
150{
151 // bypass constructors
152 static inline QLocaleId fromIds(ushort language, ushort script, ushort country)
153 {
154 const QLocaleId localeId = { .language_id: language, .script_id: script, .country_id: country };
155 return localeId;
156 }
157
158 inline bool operator==(QLocaleId other) const
159 { return language_id == other.language_id && script_id == other.script_id && country_id == other.country_id; }
160 inline bool operator!=(QLocaleId other) const
161 { return !operator==(other); }
162
163 QLocaleId withLikelySubtagsAdded() const;
164 QLocaleId withLikelySubtagsRemoved() const;
165
166 QByteArray name(char separator = '-') const;
167
168 ushort language_id, script_id, country_id;
169};
170Q_DECLARE_TYPEINFO(QLocaleId, Q_PRIMITIVE_TYPE);
171
172struct QLocaleData
173{
174public:
175 // TODO: Remove this?
176 static const QLocaleData *findLocaleData(QLocale::Language language,
177 QLocale::Script script,
178 QLocale::Country country);
179 // Having an offset of current locale, enables us to have multiple sources of data, i.e. user-provided calendar locales
180 static uint findLocaleOffset(QLocale::Language language,
181 QLocale::Script script,
182 QLocale::Country country);
183 static const QLocaleData *c();
184
185 // Maximum number of significant digits needed to represent a double.
186 // We cannot use std::numeric_limits here without constexpr.
187 static const int DoubleMantissaBits = 53;
188 static const int Log10_2_100000 = 30103; // log10(2) * 100000
189 // same as C++11 std::numeric_limits<T>::max_digits10
190 static const int DoubleMaxSignificant = (DoubleMantissaBits * Log10_2_100000) / 100000 + 2;
191
192 // Maximum number of digits before decimal point to represent a double
193 // Same as std::numeric_limits<double>::max_exponent10 + 1
194 static const int DoubleMaxDigitsBeforeDecimal = 309;
195
196 enum DoubleForm {
197 DFExponent = 0,
198 DFDecimal,
199 DFSignificantDigits,
200 _DFMax = DFSignificantDigits
201 };
202
203 enum Flags {
204 NoFlags = 0,
205 AddTrailingZeroes = 0x01,
206 ZeroPadded = 0x02,
207 LeftAdjusted = 0x04,
208 BlankBeforePositive = 0x08,
209 AlwaysShowSign = 0x10,
210 ThousandsGroup = 0x20,
211 CapitalEorX = 0x40,
212
213 ShowBase = 0x80,
214 UppercaseBase = 0x100,
215 ZeroPadExponent = 0x200,
216 ForcePoint = 0x400,
217 IndianNumberGrouping= 0x800
218 };
219
220 enum NumberMode { IntegerMode, DoubleStandardMode, DoubleScientificMode };
221
222 typedef QVarLengthArray<char, 256> CharBuff;
223
224 static QString doubleToString(const QChar zero, const QChar plus,
225 const QChar minus, const QChar exponent,
226 const QChar group, const QChar decimal,
227 double d, int precision,
228 DoubleForm form,
229 int width, unsigned flags);
230 static QString longLongToString(const QChar zero, const QChar group,
231 const QChar plus, const QChar minus,
232 qint64 l, int precision, int base,
233 int width, unsigned flags);
234 static QString unsLongLongToString(const QChar zero, const QChar group,
235 const QChar plus,
236 quint64 l, int precision,
237 int base, int width,
238 unsigned flags);
239
240 QString doubleToString(double d,
241 int precision = -1,
242 DoubleForm form = DFSignificantDigits,
243 int width = -1,
244 unsigned flags = NoFlags) const;
245 QString longLongToString(qint64 l, int precision = -1,
246 int base = 10,
247 int width = -1,
248 unsigned flags = NoFlags) const;
249 QString unsLongLongToString(quint64 l, int precision = -1,
250 int base = 10,
251 int width = -1,
252 unsigned flags = NoFlags) const;
253
254 // this function is meant to be called with the result of stringToDouble or bytearrayToDouble
255 static float convertDoubleToFloat(double d, bool *ok)
256 {
257 if (qIsInf(d))
258 return float(d);
259 if (std::fabs(x: d) > std::numeric_limits<float>::max()) {
260 if (ok)
261 *ok = false;
262 const float huge = std::numeric_limits<float>::infinity();
263 return d < 0 ? -huge : huge;
264 }
265 if (d != 0 && float(d) == 0) {
266 // Values that underflow double already failed. Match them:
267 if (ok)
268 *ok = false;
269 return 0;
270 }
271 return float(d);
272 }
273
274 double stringToDouble(QStringView str, bool *ok, QLocale::NumberOptions options) const;
275 qint64 stringToLongLong(QStringView str, int base, bool *ok, QLocale::NumberOptions options) const;
276 quint64 stringToUnsLongLong(QStringView str, int base, bool *ok, QLocale::NumberOptions options) const;
277
278 // this function is used in QIntValidator (QtGui)
279 Q_CORE_EXPORT static qint64 bytearrayToLongLong(const char *num, int base, bool *ok);
280 static quint64 bytearrayToUnsLongLong(const char *num, int base, bool *ok);
281
282 bool numberToCLocale(QStringView s, QLocale::NumberOptions number_options,
283 CharBuff *result) const;
284 inline char digitToCLocale(QChar c) const;
285
286 // this function is used in QIntValidator (QtGui)
287 Q_CORE_EXPORT bool validateChars(QStringView str, NumberMode numMode, QByteArray *buff, int decDigits = -1,
288 QLocale::NumberOptions number_options = QLocale::DefaultNumberOptions) const;
289
290public:
291 quint16 m_language_id, m_script_id, m_country_id;
292
293 // FIXME QTBUG-69324: not all unicode code-points map to single-token UTF-16 :-(
294 char16_t m_decimal, m_group, m_list, m_percent, m_zero, m_minus, m_plus, m_exponential;
295 char16_t m_quotation_start, m_quotation_end;
296 char16_t m_alternate_quotation_start, m_alternate_quotation_end;
297
298 quint16 m_list_pattern_part_start_idx, m_list_pattern_part_start_size;
299 quint16 m_list_pattern_part_mid_idx, m_list_pattern_part_mid_size;
300 quint16 m_list_pattern_part_end_idx, m_list_pattern_part_end_size;
301 quint16 m_list_pattern_part_two_idx, m_list_pattern_part_two_size;
302 quint16 m_short_date_format_idx, m_short_date_format_size;
303 quint16 m_long_date_format_idx, m_long_date_format_size;
304 quint16 m_short_time_format_idx, m_short_time_format_size;
305 quint16 m_long_time_format_idx, m_long_time_format_size;
306 quint16 m_standalone_short_day_names_idx, m_standalone_short_day_names_size;
307 quint16 m_standalone_long_day_names_idx, m_standalone_long_day_names_size;
308 quint16 m_standalone_narrow_day_names_idx, m_standalone_narrow_day_names_size;
309 quint16 m_short_day_names_idx, m_short_day_names_size;
310 quint16 m_long_day_names_idx, m_long_day_names_size;
311 quint16 m_narrow_day_names_idx, m_narrow_day_names_size;
312 quint16 m_am_idx, m_am_size;
313 quint16 m_pm_idx, m_pm_size;
314 quint16 m_byte_idx, m_byte_size;
315 quint16 m_byte_si_quantified_idx, m_byte_si_quantified_size;
316 quint16 m_byte_iec_quantified_idx, m_byte_iec_quantified_size;
317 char m_currency_iso_code[3];
318 quint16 m_currency_symbol_idx, m_currency_symbol_size;
319 quint16 m_currency_display_name_idx, m_currency_display_name_size;
320 quint8 m_currency_format_idx, m_currency_format_size;
321 quint8 m_currency_negative_format_idx, m_currency_negative_format_size;
322 quint16 m_language_endonym_idx, m_language_endonym_size;
323 quint16 m_country_endonym_idx, m_country_endonym_size;
324 quint16 m_currency_digits : 2;
325 quint16 m_currency_rounding : 3;
326 quint16 m_first_day_of_week : 3;
327 quint16 m_weekend_start : 3;
328 quint16 m_weekend_end : 3;
329};
330
331class Q_CORE_EXPORT QLocalePrivate // A POD type
332{
333public:
334 static QLocalePrivate *create(
335 const QLocaleData *data, const uint data_offset = 0,
336 QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions)
337 {
338 auto *retval = new QLocalePrivate;
339 retval->m_data = data;
340 retval->ref.storeRelaxed(newValue: 0);
341 retval->m_data_offset = data_offset;
342 retval->m_numberOptions = numberOptions;
343 return retval;
344 }
345
346 static QLocalePrivate *get(QLocale &l) { return l.d; }
347 static const QLocalePrivate *get(const QLocale &l) { return l.d; }
348
349 QChar decimal() const { return QChar(m_data->m_decimal); }
350 QChar group() const { return QChar(m_data->m_group); }
351 QChar list() const { return QChar(m_data->m_list); }
352 QChar percent() const { return QChar(m_data->m_percent); }
353 QChar zero() const { return QChar(m_data->m_zero); }
354 QChar plus() const { return QChar(m_data->m_plus); }
355 QChar minus() const { return QChar(m_data->m_minus); }
356 QChar exponential() const { return QChar(m_data->m_exponential); }
357
358 quint16 languageId() const { return m_data->m_language_id; }
359 quint16 countryId() const { return m_data->m_country_id; }
360
361 QByteArray bcp47Name(char separator = '-') const;
362
363 inline QLatin1String languageCode() const { return languageToCode(language: QLocale::Language(m_data->m_language_id)); }
364 inline QLatin1String scriptCode() const { return scriptToCode(script: QLocale::Script(m_data->m_script_id)); }
365 inline QLatin1String countryCode() const { return countryToCode(country: QLocale::Country(m_data->m_country_id)); }
366
367 static QLatin1String languageToCode(QLocale::Language language);
368 static QLatin1String scriptToCode(QLocale::Script script);
369 static QLatin1String countryToCode(QLocale::Country country);
370 static QLocale::Language codeToLanguage(QStringView code) noexcept;
371 static QLocale::Script codeToScript(QStringView code) noexcept;
372 static QLocale::Country codeToCountry(QStringView code) noexcept;
373 static void getLangAndCountry(const QString &name, QLocale::Language &lang,
374 QLocale::Script &script, QLocale::Country &cntry);
375
376 QLocale::MeasurementSystem measurementSystem() const;
377
378 const QLocaleData *m_data;
379 QBasicAtomicInt ref;
380 uint m_data_offset;
381 QLocale::NumberOptions m_numberOptions;
382};
383
384#ifndef QT_NO_SYSTEMLOCALE
385const QLocaleData *QSystemLocale::fallbackUiLocaleData() const { return fallbackUiLocale().d->m_data; }
386#endif
387
388template <>
389inline QLocalePrivate *QSharedDataPointer<QLocalePrivate>::clone()
390{
391 // cannot use QLocalePrivate's copy constructor
392 // since it is deleted in C++11
393 return QLocalePrivate::create(data: d->m_data, data_offset: d->m_data_offset, numberOptions: d->m_numberOptions);
394}
395
396inline char QLocaleData::digitToCLocale(QChar in) const
397{
398 const ushort tenUnicode = m_zero + 10;
399
400 if (in.unicode() >= m_zero && in.unicode() < tenUnicode)
401 return '0' + in.unicode() - m_zero;
402
403 if (in.unicode() >= '0' && in.unicode() <= '9')
404 return in.toLatin1();
405
406 if (in == m_plus || in == QLatin1Char('+'))
407 return '+';
408
409 if (in == m_minus || in == QLatin1Char('-') || in == QChar(0x2212))
410 return '-';
411
412 if (in == m_decimal)
413 return '.';
414
415 if (in == m_group)
416 return ',';
417
418 if (in == m_exponential || in == QChar(QChar::toUpper(ucs4: m_exponential)))
419 return 'e';
420
421 // In several languages group() is a non-breaking space (U+00A0) or its thin
422 // version (U+202f), which look like spaces. People (and thus some of our
423 // tests) use a regular space instead and complain if it doesn't work.
424 if ((m_group == 0xA0 || m_group == 0x202f) && in.unicode() == ' ')
425 return ',';
426
427 return 0;
428}
429
430QString qt_readEscapedFormatString(QStringView format, int *idx);
431bool qt_splitLocaleName(const QString &name, QString &lang, QString &script, QString &cntry);
432int qt_repeatCount(QStringView s);
433
434enum { AsciiSpaceMask = (1u << (' ' - 1)) |
435 (1u << ('\t' - 1)) | // 9: HT - horizontal tab
436 (1u << ('\n' - 1)) | // 10: LF - line feed
437 (1u << ('\v' - 1)) | // 11: VT - vertical tab
438 (1u << ('\f' - 1)) | // 12: FF - form feed
439 (1u << ('\r' - 1)) }; // 13: CR - carriage return
440Q_DECL_CONSTEXPR inline bool ascii_isspace(uchar c)
441{
442 return c >= 1u && c <= 32u && (AsciiSpaceMask >> uint(c - 1)) & 1u;
443}
444
445#if defined(Q_COMPILER_CONSTEXPR)
446Q_STATIC_ASSERT(ascii_isspace(' '));
447Q_STATIC_ASSERT(ascii_isspace('\t'));
448Q_STATIC_ASSERT(ascii_isspace('\n'));
449Q_STATIC_ASSERT(ascii_isspace('\v'));
450Q_STATIC_ASSERT(ascii_isspace('\f'));
451Q_STATIC_ASSERT(ascii_isspace('\r'));
452Q_STATIC_ASSERT(!ascii_isspace('\0'));
453Q_STATIC_ASSERT(!ascii_isspace('\a'));
454Q_STATIC_ASSERT(!ascii_isspace('a'));
455Q_STATIC_ASSERT(!ascii_isspace('\177'));
456Q_STATIC_ASSERT(!ascii_isspace(uchar('\200')));
457Q_STATIC_ASSERT(!ascii_isspace(uchar('\xA0')));
458Q_STATIC_ASSERT(!ascii_isspace(uchar('\377')));
459#endif
460
461QT_END_NAMESPACE
462
463Q_DECLARE_METATYPE(QStringRef)
464Q_DECLARE_METATYPE(QList<Qt::DayOfWeek>)
465#ifndef QT_NO_SYSTEMLOCALE
466Q_DECLARE_METATYPE(QSystemLocale::CurrencyToStringArgument)
467#endif
468
469#endif // QLOCALE_P_H
470

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