Revision control
++ // Copyright (C) 2021 The Qt Company Ltd.
++ // Copyright (C) 2013 Aleix Pol Gonzalez <aleixpol@kde.org>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:critical reason:data-parser
++
++ #include "qcollator_p.h"
++ #include "qstringlist.h"
++ #include "qstring.h"
++
++ #include "qdebug.h"
++ #include "qlocale_p.h"
++ #include "qthreadstorage.h"
++
++ QT_BEGIN_NAMESPACE
++ QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QCollatorSortKeyPrivate)
++ Q_LOGGING_CATEGORY(lcQCollator, "qt.core.qcollator")
++
++ namespace {
++ struct GenerationalCollator
++ {
++ QCollator theCollator;
++ int generation = QLocalePrivate::s_generation.loadRelaxed();
++ public:
++ GenerationalCollator() = default;
++ GenerationalCollator(const QCollator ©) : theCollator(copy) {}
++ QCollator &collator()
++ {
++ int currentGeneration = QLocalePrivate::s_generation.loadRelaxed();
++ if (Q_UNLIKELY(generation != currentGeneration)) {
++ // reinitialize the collator
++ generation = currentGeneration;
++ theCollator = QCollator();
++ }
++ return theCollator;
++ }
++ };
++ class GenerationalCollatorHolder
++ : QThreadStorage<GenerationalCollator>
++ {
++ public:
++ ~GenerationalCollatorHolder()
++ {
++ // Delete local data when the Q_GLOBAL_STATIC is destroyed.
++ // QThreadStorage's own cleanup for it runs too late, so we'd
++ // "leak" in an appless program.
++ clearLocalData();
++ }
++
++ using QThreadStorage<GenerationalCollator>::localData;
++ };
++ }
++ Q_GLOBAL_STATIC(GenerationalCollatorHolder, defaultCollator)
++
++ /*!
++ \class QCollator
++ \inmodule QtCore
++ \brief The QCollator class compares strings according to a localized collation algorithm.
++ \compares equality
++
++ \since 5.2
++
++ \reentrant
++ \ingroup i18n
++ \ingroup string-processing
++ \ingroup shared
++
++ QCollator is initialized with a QLocale. It can then be used to compare and
++ sort strings by using the ordering appropriate for that locale.
++
++ A QCollator object can be used together with template-based sorting
++ algorithms, such as std::sort(), to sort a list with QString entries.
++
++ \snippet code/src_corelib_text_qcollator.cpp 0
++
++ In addition to the locale, several optional flags can be set that influence
++ the result of the collation.
++
++ \section1 POSIX fallback implementation
++
++ On Unix systems, Qt is normally compiled to use ICU (except for \macos,
++ where Qt defaults to using an equivalent Apple API). However, if ICU was
++ not available at compile time or explicitly disabled, Qt will use a
++ fallback backend that uses the POSIX API only. This backend has several
++ limitations:
++
++ \list
++ \li Only the QLocale::c() and QLocale::system() locales are supported.
++ Consult the POSIX and C Standard Library manuals for the
++ \c{<locale.h>} header for more information on the system locale.
++ \li caseSensitivity() is not supported: only case-sensitive collation
++ can be performed.
++ \li The options set via numericMode(), ignorePunctuation(), and
++ options() are not supported.
++ \endlist
++
++ On Android systems, when not compiled to use an own copy of ICU Qt uses
++ the platform-provided ICU on Android 33 and newer. On older Android versions
++ it falls back to the Java Collator API, which has a few limitations:
++
++ \list
++ \li The IgnorePunctuation and NumericSort options are not supported.
++ \li The DiacriticInsensitive option only has an effect in combination
++ with CaseInsensitive also being set, using DiacriticInsensitive
++ without CaseInsensitive is not supported.
++ \endlist
++
++ The use of any of the unsupported options will cause a warning to be
++ printed to the application's output.
++
++ \section1 WebAssembly implementation
++
++ On WebAssembly, Qt uses the JavaScript \c{Intl.Collator} API as its
++ collation backend (unless ICU is available). This supports arbitrary
++ locales as well as numericMode(), caseSensitivity(), and ignorePunctuation().
++ \c{Intl.Collator} provides no sort-key extraction, so sortKey() produces
++ keys that delegate back to the collator when compared: their ordering
++ matches compare(), but they offer none of the performance benefit that sort
++ keys provide on the other backends (see QCollator::sortKey()).
++ */
++
++ /*!
++ \since 6.13
++ \enum QCollator::CollationOption
++
++ Options that control how strings are compared by \l QCollator.
++
++ The \macos, Windows, and ICU backends support all options, with variations
++ where indicated. On non-\macos Unix (including Linux), if ICU is not
++ available a fallback (POSIX) backend is used, which supports none of
++ these options.
++
++ \value CaseInsensitive Ignore case differences when comparing strings.
++
++ \value IgnorePunctuation Ignore punctuation and symbols when comparing strings.
++
++ \value NumericSort Sort strings containing numbers by their numeric value,
++ so that for example "file10" sorts after "file9"
++ instead of between "file1" and "file2".
++
++ \value DiacriticInsensitive Ignore diacritical marks when comparing strings,
++ so that for example "e" and "é" compare as equal.
++ Depending on the locale, this may also include
++ ligature folding such as æ == ae.
++ See \l {DiacriticInsensitive behavior details}
++ below.
++
++ \value WidthInsensitive Treat CJK fullwidth and halfwidth forms of a
++ character as equivalent; for example, fullwidth
++ "A123" and halfwidth "A123", or fullwidth
++ Katakana "ã‚«" and halfwidth "ï½¶".
++ See \l {WidthInsensitive behavior details} below.
++
++ \section2 DiacriticInsensitive behavior details
++
++ This option is primarily intended for search and matching, where the user
++ may not type diacritics. For example, typing "resume" in a search field and
++ expecting to find "résumé".
++
++ The exact behavior depends on the locale and platform backend:
++
++ \list
++ \li Characters that are independent letters in a given alphabet are
++ not treated as diacritic variants. For example, in Swedish "Ã¥",
++ "ä" and "ö" are separate letters that sort after "z", so they
++ do not compare equal to "a" or "o", even with this option set.
++ Their position in the alphabet is also preserved. The same
++ characters may behave differently in another locale: in German,
++ "ö" and "ä" are treated as variants of "o" and "a" and do compare
++ equal to them with this option set.
++ \li Collation folding for locale-specific equivalences
++ (such as å == aa in Norwegian) is locale-dependent and works
++ consistently across backends. Note that this maps "Ã¥" to the
++ digraph "aa", not to a single "a"; "Ã¥" is an independent letter
++ and never folds to "a".
++ \li Ligature folding (such as ß == ss and æ == ae) is applied on
++ Windows and ICU, where the locale defines it; for example
++ "æ" folds to "ae" in English but is an independent letter in
++ Norwegian and does not fold. This folding is not supported by
++ the \macos backend. On Windows, some ligature folding is applied
++ implicitly, even without this option set.
++ \endlist
++
++ \section2 WidthInsensitive behavior details
++
++ Chinese, Japanese and Korean (CJK) characters are wider than those of
++ Latin-derived scripts. For compatibility, Unicode includes "fullwidth"
++ forms of European characters that have the same width as CJK characters,
++ as well as "halfwidth" forms of some CJK characters (for example,
++ Katakana) that have the narrower European width. These width variants are
++ formally separate characters in Unicode, but represent the same letter
++ or symbol. Users may enter text using input methods that produce either
++ form, and expect them to match when searching or comparing. This option
++ makes the fullwidth and halfwidth forms of the same character compare
++ as equal.
++
++ The exact behavior depends on the platform backend:
++
++ \list
++ \li On ICU, width sensitivity and case sensitivity are controlled
++ by the same collation strength level. As a result, in non-CJK
++ locales, this option has no effect unless CaseInsensitive option
++ is set. On the \macos and Windows backends, this option works
++ independently of case sensitivity.
++ \li In CJK locales, fullwidth and halfwidth forms may already
++ compare as equal on \macos and ICU backends due to
++ locale-specific collation rules, even without this option set.
++ On Windows, this option must be set explicitly.
++ \li The native Windows backend (without ICU) uses NLS
++ {National Language Support}) to handle language-specific sorting,
++ which gives a different default ordering of fullwidth and halfwidth
++ characters than
++ {Unicode Collation Algorithm} based backends (\macos, ICU).
++ Setting this option makes them compare as equal on both.
++ \endlist
++
++ \sa setOptions(), options()
++ */
++
++ /*!
++ \since 5.13
++
++ Constructs a QCollator using the default locale's collation locale.
++
++ The system locale, when used as default locale, may have a collation locale
++ other than itself (e.g. on Unix, if LC_COLLATE is set differently to LANG in
++ the environment). All other locales are their own collation locales.
++
++ \sa setLocale(), QLocale::collation(), QLocale::setDefault()
++ */
++ QCollator::QCollator()
++ : d(nullptr)
++ {
++ }
++
++ /*!
++ Constructs a QCollator using the given \a locale.
++
++ \sa setLocale()
++ */
++ QCollator::QCollator(const QLocale &locale)
++ : d(new QCollatorPrivate(locale))
++ {
++ }
++
++ /*!
++ Creates a copy of \a other.
++ */
++ QCollator::QCollator(const QCollator &other)
++ : d(other.d)
++ {
++ if (d) {
++ // Ensure clean, lest both copies try to init() at the same time:
++ d->ensureInitialized();
++ d->ref.ref();
++ }
++ }
++
++ /*!
++ Destroys this collator.
++ */
++ QCollator::~QCollator()
++ {
++ if (d && !d->ref.deref())
++ delete d;
++ }
++
++ /*!
++ Assigns \a other to this collator.
++ */
++ QCollator &QCollator::operator=(const QCollator &other)
++ {
++ if (this != &other) {
++ if (d && !d->ref.deref())
++ delete d;
++ d = other.d;
++ if (d) {
++ // Ensure clean, lest both copies try to init() at the same time:
++ d->ensureInitialized();
++ d->ref.ref();
++ }
++ }
++ return *this;
++ }
++
++ /*!
++ \fn QCollator::QCollator(QCollator &&other)
++
++ Move constructor. Moves from \a other into this collator.
++
++ //! [partially-formed]
++ \note The moved-from object \a other is placed in a partially-formed state,
++ in which the only valid operations are destruction and assignment of a new
++ value.
++ //! [partially-formed]
++ */
++
++ /*!
++ \fn QCollator & QCollator::operator=(QCollator && other)
++
++ Move-assigns \a other to this QCollator instance.
++
++ \include qcollator.cpp partially-formed
++ */
++
++ /*!
++ \fn void QCollator::swap(QCollator &other)
++ \memberswap{collator}
++ */
++
++ /*!
++ \fn bool QCollator::operator==(const QCollator &lhs, const QCollator &rhs) noexcept
++ \fn bool QCollator::operator!=(const QCollator &lhs, const QCollator &rhs) noexcept
++ \since 6.12
++
++ Returns \c true if \a lhs and \a rhs use the same locale and collation
++ options, otherwise returns \c false.
++ */
++
++ bool comparesEqual(const QCollator &lhs, const QCollator &rhs) noexcept
++ {
++ if (lhs.d == rhs.d)
++ return true;
++ if (!lhs.d || !rhs.d)
++ return false;
++
++ return lhs.d->options == rhs.d->options
++ && lhs.d->locale == rhs.d->locale;
++ }
++
++ /*!
++ \internal
++ */
++ void QCollator::detach()
++ {
++ if (!d) {
++ d = new QCollatorPrivate(QLocale().collation());
++ d->init();
++ } else if (d->ref.loadRelaxed() != 1) {
++ QCollatorPrivate *x = new QCollatorPrivate(d->locale);
++ x->options = d->options;
++ if (!d->ref.deref())
++ delete d;
++ d = x;
++ }
++ // All callers need this, because about to modify the object:
++ d->dirty = true;
++ }
++
++ /*!
++ Sets the locale of the collator to \a locale.
++
++ \sa locale()
++ */
++ void QCollator::setLocale(const QLocale &locale)
++ {
++ if (locale == this->locale())
++ return;
++
++ detach();
++ d->locale = locale;
++ }
++
++ /*!
++ Returns the locale of the collator.
++
++ Unless supplied to the constructor or by calling setLocale(), the system's
++ default collation locale is used.
++
++ \sa setLocale(), QLocale::collation()
++ */
++ QLocale QCollator::locale() const
++ {
++ return d ? d->locale : QLocale().collation();
++ }
++
++ /*!
++ Sets the case-sensitivity of the collator to \a cs.
++
++ \sa caseSensitivity()
++ */
++ void QCollator::setCaseSensitivity(Qt::CaseSensitivity cs)
++ {
++ if (cs == caseSensitivity())
++ return;
++
++ detach();
++ d->options.setFlag(CollationOption::CaseInsensitive, cs == Qt::CaseInsensitive);
++ }
++
++ /*!
++ Returns case sensitivity of the collator.
++
++ This defaults to case-sensitive until set.
++
++ \note In the C locale, when case-sensitive, all lower-case letters sort
++ after all upper-case letters, where most locales sort each lower-case letter
++ either immediately before or immediately after its upper-case partner. Thus
++ "Zap" sorts before "ape" in the C locale but after in most others.
++
++ \sa setCaseSensitivity()
++ */
++ Qt::CaseSensitivity QCollator::caseSensitivity() const
++ {
++ return d && d->options.testFlag(CollationOption::CaseInsensitive) ? Qt::CaseInsensitive
++ : Qt::CaseSensitive;
++ }
++
++ /*!
++ Enables numeric sorting mode when \a on is \c true.
++
++ \sa numericMode()
++ */
++ void QCollator::setNumericMode(bool on)
++ {
++ if (on == numericMode())
++ return;
++
++ detach();
++ d->options.setFlag(CollationOption::NumericSort, on);
++ }
++
++ /*!
++ Returns \c true if numeric sorting is enabled, \c false otherwise.
++
++ When \c true, numerals are recognized as numbers and sorted in arithmetic
++ order; for example, 100 sortes after 99. When \c false, numbers are sorted
++ in lexical order, so that 100 sorts before 99 (because 1 is before 9). By
++ default, this option is disabled.
++
++ \sa setNumericMode()
++ */
++ bool QCollator::numericMode() const
++ {
++ return d && d->options.testFlag(CollationOption::NumericSort);
++ }
++
++ /*!
++ Ignores punctuation and symbols if \a on is \c true, attends to them if \c false.
++
++ \sa ignorePunctuation()
++ */
++ void QCollator::setIgnorePunctuation(bool on)
++ {
++ if (on == ignorePunctuation())
++ return;
++
++ detach();
++ d->options.setFlag(CollationOption::IgnorePunctuation, on);
++ }
++
++ /*!
++ Returns whether punctuation and symbols are ignored when collating.
++
++ When \c true, strings are compared as if all punctuation and symbols were
++ removed from each string.
++
++ \sa setIgnorePunctuation()
++ */
++ bool QCollator::ignorePunctuation() const
++ {
++ return d && d->options.testFlag(CollationOption::IgnorePunctuation);
++ }
++
++ /*!
++ \since 6.13
++ Sets the collation options to \a options.
++ This allows configuring multiple collation settings at once.
++
++ \note In the C locale, the collation options have no effect.
++ Use a specific locale to enable locale-aware collation.
++
++ \sa options(), CollationOption
++ */
++ void QCollator::setOptions(CollationOptions options)
++ {
++ if (d && d->options == options)
++ return;
++ detach();
++ d->options = options;
++ }
++
++ /*!
++ \since 6.13
++ Returns the collation options currently set on the collator.
++
++ \sa setOptions(), CollationOption
++ */
++ QCollator::CollationOptions QCollator::options() const
++ {
++ return d ? d->options : CollationOptions{};
++ }
++
++ /*!
++ \since 5.13
++ \fn bool QCollator::operator()(QStringView s1, QStringView s2) const
++
++ A QCollator can be used as the comparison function of a sorting algorithm.
++ It returns \c true if \a s1 sorts before \a s2, otherwise \c false.
++
++ \sa compare()
++ */
++
++ /*!
++ \since 5.13
++ \fn int QCollator::compare(QStringView s1, QStringView s2) const
++
++ Compares \a s1 with \a s2.
++
++ Returns a negative integer if \a s1 is less than \a s2, a positive integer
++ if it is greater than \a s2, and zero if they are equal.
++ */
++
++ /*!
++ \fn bool QCollator::operator()(const QString &s1, const QString &s2) const
++ \overload
++ \since 5.2
++ */
++
++ /*!
++ \fn int QCollator::compare(const QString &s1, const QString &s2) const
++ \overload
++ \since 5.2
++ */
++
++ /*!
++ \fn int QCollator::compare(const QChar *s1, qsizetype len1, const QChar *s2, qsizetype len2) const
++ \overload
++ \since 5.2
++
++ Compares \a s1 with \a s2. \a len1 and \a len2 specify the lengths of the
++ QChar arrays pointed to by \a s1 and \a s2.
++
++ Returns a negative integer if \a s1 is less than \a s2, a positive integer
++ if it is greater than \a s2, and zero if they are equal.
++
++
++ \note In Qt versions prior to 6.4, the length arguments were of type
++ \c{int}, not \c{qsizetype}.
++ */
++
++ /*!
++ \since 6.3
++
++ Compares the strings \a s1 and \a s2, returning their sorting order. This
++ function performs the same operation as compare() on a default-constructed
++ QCollator object.
++
++ \sa compare(), defaultSortKey()
++ */
++ int QCollator::defaultCompare(QStringView s1, QStringView s2)
++ {
++ return defaultCollator->localData().collator().compare(s1, s2);
++ }
++
++ /*!
++ \since 6.3
++
++ Returns the sort key for the string \a key. This function performs the same
++ operation as sortKey() on a default-constructed QCollator object.
++
++ \sa sortKey(), defaultCompare()
++ */
++ QCollatorSortKey QCollator::defaultSortKey(QStringView key)
++ {
++ return defaultCollator->localData().collator().sortKey(key.toString());
++ }
++
++ /*!
++ \fn QCollatorSortKey QCollator::sortKey(const QString &string) const
++
++ Returns a sortKey for \a string.
++
++ Creating the sort key is usually somewhat slower, than using the compare()
++ methods directly. But if the string is compared repeatedly (e.g. when
++ sorting a whole list of strings), it's usually faster to create the sort
++ keys for each string and then sort using the keys.
++
++ \note Not supported with the C (a.k.a. POSIX) locale on Darwin.
++ \note Does not provide a performance improvement on Qt for WebAssembly
++ due to native API limitations.
++ */
++
++ /*!
++ \class QCollatorSortKey
++ \inmodule QtCore
++ \brief The QCollatorSortKey class can be used to speed up string collation.
++ \compares weak
++
++ \since 5.2
++
++ The QCollatorSortKey class is always created by QCollator::sortKey() and is
++ used for fast strings collation, for example when collating many strings.
++
++ \reentrant
++ \ingroup i18n
++ \ingroup string-processing
++ \ingroup shared
++
++ \sa QCollator, QCollator::sortKey(), compare()
++ */
++
++ /*!
++ \internal
++ */
++ QCollatorSortKey::QCollatorSortKey(QCollatorSortKeyPrivate *d)
++ : d(d)
++ {
++ }
++
++ /*!
++ Constructs a copy of the \a other collator key.
++ */
++ QCollatorSortKey::QCollatorSortKey(const QCollatorSortKey &other)
++ : d(other.d)
++ {
++ }
++
++ /*!
++ \since 6.8
++ \fn QCollatorSortKey::QCollatorSortKey(QCollatorSortKey &&other)
++ Move-constructs a new QCollatorSortKey from \a other.
++
++ \include qcollator.cpp partially-formed
++ */
++
++ /*!
++ Destroys the collator key.
++ */
++ QCollatorSortKey::~QCollatorSortKey()
++ {
++ }
++
++ /*!
++ Assigns \a other to this collator key.
++ */
++ QCollatorSortKey& QCollatorSortKey::operator=(const QCollatorSortKey &other)
++ {
++ if (this != &other) {
++ d = other.d;
++ }
++ return *this;
++ }
++
++ /*!
++ \fn QCollatorSortKey &QCollatorSortKey::operator=(QCollatorSortKey && other)
++
++ Move-assigns \a other to this QCollatorSortKey instance.
++
++ \include qcollator.cpp partially-formed
++ */
++
++ /*!
++ \fn bool QCollatorSortKey::operator<(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
++ \fn bool QCollatorSortKey::operator<=(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
++ \fn bool QCollatorSortKey::operator>(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
++ \fn bool QCollatorSortKey::operator>=(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
++ \fn bool QCollatorSortKey::operator==(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
++ \fn bool QCollatorSortKey::operator!=(const QCollatorSortKey &lhs, const QCollatorSortKey &rhs)
++ \since 6.12
++
++ Both keys must have been created by the same QCollator's sortKey(). Compares
++ \a lhs and \a rhs according to the QCollator that created them.
++
++ \sa QCollatorSortKey::compare()
++ */
++
++ /*!
++ \fn void QCollatorSortKey::swap(QCollatorSortKey & other)
++ \memberswap{collator key}
++ */
++
++ /*!
++ \fn int QCollatorSortKey::compare(const QCollatorSortKey &otherKey) const noexcept
++
++ Compares this key to \a otherKey, which must have been created by the same
++ QCollator's sortKey() as this key. The comparison is performed in accordance
++ with that QCollator's sort order.
++
++ Returns a negative value if this key sorts before \a otherKey, 0 if the
++ two keys are equal, or a positive value if this key sorts after \a otherKey.
++
++ \sa operator==(), operator!=(), operator<(), operator<=(),
++ operator>(), operator>=()
++ */
++
++ QT_END_NAMESPACE