diff options
Diffstat (limited to 'src/corelib/text/qlocale_p.h')
-rw-r--r-- | src/corelib/text/qlocale_p.h | 370 |
1 files changed, 198 insertions, 172 deletions
diff --git a/src/corelib/text/qlocale_p.h b/src/corelib/text/qlocale_p.h index 60bc78db04..3044d137b9 100644 --- a/src/corelib/text/qlocale_p.h +++ b/src/corelib/text/qlocale_p.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2016 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// Copyright (C) 2016 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QLOCALE_P_H #define QLOCALE_P_H @@ -52,25 +16,98 @@ // We mean it. // -#include <QtCore/private/qglobal_p.h> -#include "QtCore/qstring.h" -#include "QtCore/qvarlengtharray.h" -#include "QtCore/qvariant.h" -#include "QtCore/qnumeric.h" -#include <QtCore/qcalendar.h> - #include "qlocale.h" +#include <QtCore/qcalendar.h> +#include <QtCore/qlist.h> +#include <QtCore/qnumeric.h> +#include <QtCore/private/qnumeric_p.h> +#include <QtCore/qstring.h> +#include <QtCore/qvariant.h> +#include <QtCore/qvarlengtharray.h> +#ifdef Q_OS_WASM +#include <private/qstdweb_p.h> +#endif + #include <limits> #include <cmath> +#include <string_view> QT_BEGIN_NAMESPACE -#ifndef QT_NO_SYSTEMLOCALE +template <typename T> struct QSimpleParsedNumber +{ + T result; + // When used < 0, -used is how much was used, but it was an error. + qsizetype used; + bool ok() const { return used > 0; } +}; + +template <typename MaskType, uchar Lowest> struct QCharacterSetMatch +{ + static constexpr int MaxRange = std::numeric_limits<MaskType>::digits; + MaskType mask; + + constexpr QCharacterSetMatch(std::string_view set) + : mask(0) + { + for (char c : set) { + int idx = uchar(c) - Lowest; + mask |= MaskType(1) << idx; + } + } + + constexpr bool matches(uchar c) const + { + unsigned idx = c - Lowest; + if (idx >= MaxRange) + return false; + return (mask >> idx) & 1; + } +}; + +namespace QtPrivate { +inline constexpr char ascii_space_chars[] = + "\t" // 9: HT - horizontal tab + "\n" // 10: LF - line feed + "\v" // 11: VT - vertical tab + "\f" // 12: FF - form feed + "\r" // 13: CR - carriage return + " "; // 32: space + +template <const char *Set, int ForcedLowest = -1> +inline constexpr auto makeCharacterSetMatch() +{ + constexpr auto view = std::string_view(Set); + constexpr uchar MinElement = *std::min_element(view.begin(), view.end()); + constexpr uchar MaxElement = *std::max_element(view.begin(), view.end()); + constexpr int Range = MaxElement - MinElement; + static_assert(Range < 64, "Characters in the set are 64 or more values apart"); + + if constexpr (ForcedLowest >= 0) { + // use the force + static_assert(ForcedLowest <= int(MinElement), "The force is not with you"); + using MaskType = std::conditional_t<MaxElement - ForcedLowest < 32, quint32, quint64>; + return QCharacterSetMatch<MaskType, ForcedLowest>(view); + } else if constexpr (MaxElement < std::numeric_limits<qregisteruint>::digits) { + // if we can use a Lowest of zero, we can remove a subtraction + // from the matches() code at runtime + using MaskType = std::conditional_t<(MaxElement < 32), quint32, qregisteruint>; + return QCharacterSetMatch<MaskType, 0>(view); + } else { + using MaskType = std::conditional_t<(Range < 32), quint32, quint64>; + return QCharacterSetMatch<MaskType, MinElement>(view); + } +} +} // QtPrivate + struct QLocaleData; // Subclassed by Android platform plugin: class Q_CORE_EXPORT QSystemLocale { + Q_DISABLE_COPY_MOVE(QSystemLocale) + QSystemLocale *next = nullptr; // Maintains a stack. + public: QSystemLocale(); virtual ~QSystemLocale(); @@ -133,17 +170,13 @@ public: StandaloneDayNameShort, // QString, in: int StandaloneDayNameNarrow // QString, in: int }; - virtual QVariant query(QueryType type, QVariant in = QVariant()) const; + virtual QVariant query(QueryType type, QVariant &&in = QVariant()) const; virtual QLocale fallbackLocale() const; - inline uint fallbackLocaleIndex() const; -private: - QSystemLocale(bool); - friend class QSystemLocaleSingleton; + inline qsizetype fallbackLocaleIndex() const; }; Q_DECLARE_TYPEINFO(QSystemLocale::QueryType, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(QSystemLocale::CurrencyToStringArgument, Q_RELOCATABLE_TYPE); -#endif #if QT_CONFIG(icu) namespace QIcu { @@ -155,7 +188,7 @@ namespace QIcu { struct QLocaleId { - [[nodiscard]] static QLocaleId fromName(QStringView name); + [[nodiscard]] Q_AUTOTEST_EXPORT static QLocaleId fromName(QStringView name); [[nodiscard]] inline bool operator==(QLocaleId other) const { return language_id == other.language_id && script_id == other.script_id && territory_id == other.territory_id; } [[nodiscard]] inline bool operator!=(QLocaleId other) const @@ -191,12 +224,27 @@ struct QLocaleId }; Q_DECLARE_TYPEINFO(QLocaleId, Q_PRIMITIVE_TYPE); + +using CharBuff = QVarLengthArray<char, 256>; + +struct ParsingResult +{ + enum State { // A duplicate of QValidator::State + Invalid, + Intermediate, + Acceptable + }; + + State state = Invalid; + CharBuff buff; +}; + struct QLocaleData { public: // Having an index for each locale enables us to have diverse sources of // data, e.g. calendar locales, as well as the main CLDR-derived data. - [[nodiscard]] static int findLocaleIndex(QLocaleId localeId); + [[nodiscard]] static qsizetype findLocaleIndex(QLocaleId localeId); [[nodiscard]] static const QLocaleData *c(); enum DoubleForm { @@ -224,8 +272,6 @@ public: enum NumberMode { IntegerMode, DoubleStandardMode, DoubleScientificMode }; - typedef QVarLengthArray<char, 256> CharBuff; - private: enum PrecisionMode { PMDecimalDigits = 0x01, @@ -259,45 +305,82 @@ public: unsigned flags = NoFlags) const; // this function is meant to be called with the result of stringToDouble or bytearrayToDouble + // so *ok must have been properly set (if not null) [[nodiscard]] static float convertDoubleToFloat(double d, bool *ok) { - if (qIsInf(d)) - return float(d); - if (std::fabs(d) > std::numeric_limits<float>::max()) { - if (ok) - *ok = false; - const float huge = std::numeric_limits<float>::infinity(); - return d < 0 ? -huge : huge; - } - if (d != 0 && float(d) == 0) { - // Values that underflow double already failed. Match them: - if (ok) - *ok = false; - return 0; - } - return float(d); + float result; + bool b = convertDoubleTo<float>(d, &result); + if (ok && *ok) + *ok = b; + return result; } [[nodiscard]] double stringToDouble(QStringView str, bool *ok, QLocale::NumberOptions options) const; - [[nodiscard]] qint64 stringToLongLong(QStringView str, int base, bool *ok, - QLocale::NumberOptions options) const; - [[nodiscard]] quint64 stringToUnsLongLong(QStringView str, int base, bool *ok, - QLocale::NumberOptions options) const; + [[nodiscard]] QSimpleParsedNumber<qint64> + stringToLongLong(QStringView str, int base, QLocale::NumberOptions options) const; + [[nodiscard]] QSimpleParsedNumber<quint64> + stringToUnsLongLong(QStringView str, int base, QLocale::NumberOptions options) const; // this function is used in QIntValidator (QtGui) - [[nodiscard]] Q_CORE_EXPORT static qint64 bytearrayToLongLong(QByteArrayView num, int base, - bool *ok); - [[nodiscard]] static quint64 bytearrayToUnsLongLong(QByteArrayView num, int base, bool *ok); + [[nodiscard]] Q_CORE_EXPORT + static QSimpleParsedNumber<qint64> bytearrayToLongLong(QByteArrayView num, int base); + [[nodiscard]] static QSimpleParsedNumber<quint64> + bytearrayToUnsLongLong(QByteArrayView num, int base); [[nodiscard]] bool numberToCLocale(QStringView s, QLocale::NumberOptions number_options, - CharBuff *result) const; - [[nodiscard]] inline char numericToCLocale(QStringView in) const; + NumberMode mode, CharBuff *result) const; + + struct NumericData + { +#ifndef QT_NO_SYSTEMLOCALE + // Only used for the system locale, to store data for the view to look at: + QString sysDecimal, sysGroup, sysMinus, sysPlus; +#endif + QStringView decimal, group, minus, plus, exponent; + char32_t zeroUcs = 0; + qint8 zeroLen = 0; + bool isC = false; // C locale sets this and nothing else. + bool exponentCyrillic = false; // True only for floating-point parsing of Cyrillic. + void setZero(QStringView zero) + { + // No known locale has digits that are more than one Unicode + // code-point, so we can safely deal with digits as plain char32_t. + switch (zero.size()) { + case 1: + Q_ASSERT(!zero.at(0).isSurrogate()); + zeroUcs = zero.at(0).unicode(); + zeroLen = 1; + break; + case 2: + Q_ASSERT(zero.at(0).isHighSurrogate()); + zeroUcs = QChar::surrogateToUcs4(zero.at(0), zero.at(1)); + zeroLen = 2; + break; + default: + Q_ASSERT(zero.size() == 0); // i.e. we got no value to use + break; + } + } + [[nodiscard]] bool isValid(NumberMode mode) const // Asserted as a sanity check. + { + if (isC) + return true; + if (exponentCyrillic && exponent != u"E" && exponent != u"\u0415") + return false; + return (zeroLen == 1 || zeroLen == 2) && zeroUcs > 0 + && (mode == IntegerMode || !decimal.isEmpty()) + // group may be empty (user config in system locale) + && !minus.isEmpty() && !plus.isEmpty() + && (mode != DoubleScientificMode || !exponent.isEmpty()); + } + }; + [[nodiscard]] inline NumericData numericData(NumberMode mode) const; // this function is used in QIntValidator (QtGui) - [[nodiscard]] Q_CORE_EXPORT bool validateChars( - QStringView str, NumberMode numMode, QByteArray *buff, int decDigits = -1, - QLocale::NumberOptions number_options = QLocale::DefaultNumberOptions) const; + [[nodiscard]] Q_CORE_EXPORT ParsingResult + validateChars(QStringView str, NumberMode numMode, int decDigits = -1, + QLocale::NumberOptions number_options = QLocale::DefaultNumberOptions) const; // Access to assorted data members: [[nodiscard]] QLocaleId id() const @@ -327,11 +410,11 @@ public: { return { reinterpret_cast<const QChar *>(table + offset), size }; } - [[nodiscard]] QString getListEntry(const char16_t *table, int index) const + [[nodiscard]] QString getListEntry(const char16_t *table, qsizetype index) const { return listEntry(table, index).getData(table); } - [[nodiscard]] QStringView viewListEntry(const char16_t *table, int index) const + [[nodiscard]] QStringView viewListEntry(const char16_t *table, qsizetype index) const { return listEntry(table, index).viewData(table); } @@ -344,7 +427,7 @@ public: return 0; } private: - [[nodiscard]] DataRange listEntry(const char16_t *table, int index) const + [[nodiscard]] DataRange listEntry(const char16_t *table, qsizetype index) const { const char16_t separator = ';'; quint16 i = 0; @@ -399,7 +482,7 @@ public: quint8 m_first_day_of_week : 3; quint8 m_weekend_start : 3; quint8 m_weekend_end : 3; - quint8 m_grouping_top : 2; // Must have this many before the first grouping separator + quint8 m_grouping_top : 2; // Don't group until more significant group has this many digits. quint8 m_grouping_higher : 3; // Number of digits between grouping separators quint8 m_grouping_least : 3; // Number of digits after last grouping separator (before decimal). }; @@ -407,7 +490,7 @@ public: class QLocalePrivate { public: - constexpr QLocalePrivate(const QLocaleData *data, const uint index, + constexpr QLocalePrivate(const QLocaleData *data, qsizetype index, QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions, int refs = 0) : m_data(data), ref Q_BASIC_ATOMIC_INITIALIZER(refs), @@ -418,18 +501,25 @@ public: [[nodiscard]] QByteArray bcp47Name(char separator = '-') const; - [[nodiscard]] inline QLatin1String languageCode() const - { return languageToCode(QLocale::Language(m_data->m_language_id)); } - [[nodiscard]] inline QLatin1String scriptCode() const + [[nodiscard]] inline std::array<char, 4> + languageCode(QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode) const + { + return languageToCode(QLocale::Language(m_data->m_language_id), codeTypes); + } + [[nodiscard]] inline QLatin1StringView scriptCode() const { return scriptToCode(QLocale::Script(m_data->m_script_id)); } - [[nodiscard]] inline QLatin1String territoryCode() const + [[nodiscard]] inline QLatin1StringView territoryCode() const { return territoryToCode(QLocale::Territory(m_data->m_territory_id)); } [[nodiscard]] static const QLocalePrivate *get(const QLocale &l) { return l.d; } - [[nodiscard]] static QLatin1String languageToCode(QLocale::Language language); - [[nodiscard]] static QLatin1String scriptToCode(QLocale::Script script); - [[nodiscard]] static QLatin1String territoryToCode(QLocale::Territory territory); - [[nodiscard]] static QLocale::Language codeToLanguage(QStringView code) noexcept; + [[nodiscard]] static std::array<char, 4> + languageToCode(QLocale::Language language, + QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode); + [[nodiscard]] static QLatin1StringView scriptToCode(QLocale::Script script); + [[nodiscard]] static QLatin1StringView territoryToCode(QLocale::Territory territory); + [[nodiscard]] static QLocale::Language + codeToLanguage(QStringView code, + QLocale::LanguageCodeTypes codeTypes = QLocale::AnyLanguageCode) noexcept; [[nodiscard]] static QLocale::Script codeToScript(QStringView code) noexcept; [[nodiscard]] static QLocale::Territory codeToTerritory(QStringView code) noexcept; @@ -438,14 +528,14 @@ public: // System locale has an m_data all its own; all others have m_data = locale_data + m_index const QLocaleData *const m_data; QBasicAtomicInt ref; - const uint m_index; + qsizetype m_index; // System locale needs this updated when m_data->id() changes. QLocale::NumberOptions m_numberOptions; static QBasicAtomicInt s_generation; }; #ifndef QT_NO_SYSTEMLOCALE -uint QSystemLocale::fallbackLocaleIndex() const { return fallbackLocale().d->m_index; } +qsizetype QSystemLocale::fallbackLocaleIndex() const { return fallbackLocale().d->m_index; } #endif template <> @@ -456,90 +546,26 @@ inline QLocalePrivate *QSharedDataPointer<QLocalePrivate>::clone() return new QLocalePrivate(d->m_data, d->m_index, d->m_numberOptions); } -inline char QLocaleData::numericToCLocale(QStringView in) const -{ - Q_ASSERT(in.size() == 1 || (in.size() == 2 && in.at(0).isHighSurrogate())); - - if (in == positiveSign() || in == u"+") - return '+'; - - if (in == negativeSign() || in == u"-" || in == u"\x2212") - return '-'; - - if (in == decimalPoint()) - return '.'; - - if (in.compare(exponentSeparator(), Qt::CaseInsensitive) == 0) - return 'e'; - - const QString group = groupSeparator(); - if (in == group) - return ','; - - // In several languages group() is a non-breaking space (U+00A0) or its thin - // version (U+202f), which look like spaces. People (and thus some of our - // tests) use a regular space instead and complain if it doesn't work. - // Should this be extended generally to any case where group is a space ? - if ((group == u"\xa0" || group == u"\x202f") && in == u" ") - return ','; - - const char32_t inUcs4 = in.size() == 2 - ? QChar::surrogateToUcs4(in.at(0), in.at(1)) : in.at(0).unicode(); - const char32_t zeroUcs4 = zeroUcs(); - // Must match qlocale_tools.h's unicodeForDigit() - if (zeroUcs4 == u'\u3007') { - // QTBUG-85409: Suzhou's digits aren't contiguous ! - if (inUcs4 == zeroUcs4) - return '0'; - if (inUcs4 > u'\u3020' && inUcs4 <= u'\u3029') - return inUcs4 - u'\u3020'; - } else if (zeroUcs4 <= inUcs4 && inUcs4 < zeroUcs4 + 10) { - return '0' + inUcs4 - zeroUcs4; - } - if ('0' <= inUcs4 && inUcs4 <= '9') - return inUcs4; - - return 0; -} - // Also used to merely skip over an escape in a format string, advancint idx to // point after it (so not [[nodiscard]]): -QString qt_readEscapedFormatString(QStringView format, int *idx); +QString qt_readEscapedFormatString(QStringView format, qsizetype *idx); [[nodiscard]] bool qt_splitLocaleName(QStringView name, QStringView *lang = nullptr, QStringView *script = nullptr, QStringView *cntry = nullptr); -[[nodiscard]] int qt_repeatCount(QStringView s); - -enum { AsciiSpaceMask = (1u << (' ' - 1)) | - (1u << ('\t' - 1)) | // 9: HT - horizontal tab - (1u << ('\n' - 1)) | // 10: LF - line feed - (1u << ('\v' - 1)) | // 11: VT - vertical tab - (1u << ('\f' - 1)) | // 12: FF - form feed - (1u << ('\r' - 1)) }; // 13: CR - carriage return +[[nodiscard]] qsizetype qt_repeatCount(QStringView s); + [[nodiscard]] constexpr inline bool ascii_isspace(uchar c) { - return c >= 1u && c <= 32u && (AsciiSpaceMask >> uint(c - 1)) & 1u; + constexpr auto matcher = QtPrivate::makeCharacterSetMatch<QtPrivate::ascii_space_chars>(); + return matcher.matches(c); } -static_assert(ascii_isspace(' ')); -static_assert(ascii_isspace('\t')); -static_assert(ascii_isspace('\n')); -static_assert(ascii_isspace('\v')); -static_assert(ascii_isspace('\f')); -static_assert(ascii_isspace('\r')); -static_assert(!ascii_isspace('\0')); -static_assert(!ascii_isspace('\a')); -static_assert(!ascii_isspace('a')); -static_assert(!ascii_isspace('\177')); -static_assert(!ascii_isspace(uchar('\200'))); -static_assert(!ascii_isspace(uchar('\xA0'))); -static_assert(!ascii_isspace(uchar('\377'))); - QT_END_NAMESPACE -Q_DECLARE_METATYPE(QStringView) -Q_DECLARE_METATYPE(QList<Qt::DayOfWeek>) +// ### move to qnamespace.h +QT_DECL_METATYPE_EXTERN_TAGGED(QList<Qt::DayOfWeek>, QList_Qt__DayOfWeek, Q_CORE_EXPORT) #ifndef QT_NO_SYSTEMLOCALE -Q_DECLARE_METATYPE(QSystemLocale::CurrencyToStringArgument) +QT_DECL_METATYPE_EXTERN_TAGGED(QSystemLocale::CurrencyToStringArgument, + QSystemLocale__CurrencyToStringArgument, Q_CORE_EXPORT) #endif #endif // QLOCALE_P_H |