diff options
author | Marc Mutz <marc.mutz@qt.io> | 2022-01-28 11:23:55 +0100 |
---|---|---|
committer | Marc Mutz <marc.mutz@qt.io> | 2022-01-30 14:18:36 +0000 |
commit | 2e4db2596a69fe9efd0d72a4da7d588716ad2a59 (patch) | |
tree | 6342370a25fbc7bb8dfbe822cc7f2318994b51a5 | |
parent | 9135531c33551e03ef2b7287830228def0a81f5a (diff) |
QCalendar: port registry from QHash to QFlatMap
Unlike many other uses of QFlatMap, this actually promises to hit the
QFlatMap sweet spot, and we're using all its levers.
To wit:
- Enable lookup via QAnyStringView through a transparent comparator
without having to convert the QAnyStringView to QString first. This
is impossible with QHash, because qHash() values are not consistent
for different string types: u"a"_qsv == "a"_L1, but qHash(u"a"_qsv)
!= qHash("a"_L1). The relational operators don't have this
consistency problem, and they also implement case-insensitive
comparison, another thing qHash lacks.
- Pick different types of containers for keys and values. For the
keys, use QStringList, which makes availableCalendars() trivial and
extremely fast. We can reserve() the flat_map to limit the effect of
reallocations, because we have a pretty good idea about how many
entries we'll have. For the values, use the same container as for
byID. This might be better with QVarLengthArray, but that's for
another patch.
- Fix the double lookup in registerBackendLockHeld() by using
try_emplace() instead of contains() + insert().
To enable QFlatMap's full potential, we still need to teach
ensurePopulated() to create the two containers for the QFlatMap
unordered and let QFlatMap sort them in one go, but that, too, is for
another patch.
Change-Id: I7fe4f3f7596e9b234696fbc8e467128b85629f8a
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r-- | src/corelib/time/qcalendar.cpp | 56 |
1 files changed, 28 insertions, 28 deletions
diff --git a/src/corelib/time/qcalendar.cpp b/src/corelib/time/qcalendar.cpp index 3c82759b19..a7f6a634d6 100644 --- a/src/corelib/time/qcalendar.cpp +++ b/src/corelib/time/qcalendar.cpp @@ -50,6 +50,7 @@ #include "qislamiccivilcalendar_p.h" #endif +#include <private/qflatmap_p.h> #include "qatomic.h" #include "qdatetime.h" #include "qcalendarmath_p.h" @@ -62,22 +63,15 @@ QT_BEGIN_NAMESPACE namespace { -struct CalendarName : public QString -{ - CalendarName(const QString &name) : QString(name) {} +struct CaseInsensitiveAnyStringViewLessThan { + struct is_transparent {}; + bool operator()(QAnyStringView lhs, QAnyStringView rhs) const + { + return QAnyStringView::compare(lhs, rhs, Qt::CaseInsensitive) < 0; + } }; -inline bool operator==(const CalendarName &u, const CalendarName &v) -{ - return u.compare(v, Qt::CaseInsensitive) == 0; -} - -inline size_t qHash(const CalendarName &key, size_t seed = 0) noexcept -{ - return qHash(key.toLower(), seed); -} - -} // anonymous namespace +} // unnamed namespace namespace QtPrivate { @@ -89,6 +83,8 @@ class QCalendarRegistry { Q_DISABLE_COPY_MOVE(QCalendarRegistry); // This is a singleton. + static constexpr qsizetype ExpectedNumberOfBackends = qsizetype(QCalendar::System::Last) + 1; + /* Lock protecting the registry from concurrent modification. */ @@ -108,7 +104,12 @@ class QCalendarRegistry Each backend may be registered with several names associated with it. The names are case-insensitive. */ - QHash<CalendarName, QCalendarBackend *> byName; + QFlatMap< + QString, QCalendarBackend *, + CaseInsensitiveAnyStringViewLessThan, + QStringList, + std::vector<QCalendarBackend *> + > byName; /* Pointer to the Gregorian backend for faster lockless access to it. @@ -138,7 +139,11 @@ class QCalendarRegistry QCalendar::System system); public: - QCalendarRegistry() { byId.resize(int(QCalendar::System::Last) + 1); } + QCalendarRegistry() + { + byId.resize(ExpectedNumberOfBackends); + byName.reserve(ExpectedNumberOfBackends * 2); // assume one alias on average + } ~QCalendarRegistry(); @@ -339,12 +344,11 @@ void QCalendarRegistry::registerBackendLockHeld(QCalendarBackend *backend, const // Register any names. for (const auto &name : names) { - if (byName.contains(name)) { + auto [it, inserted] = byName.try_emplace(name, backend); + if (!inserted) { Q_ASSERT(system == QCalendar::System::User); qWarning("Cannot register name %ls (already in use) for %ls", qUtf16Printable(name), qUtf16Printable(backend->name())); - } else { - byName[name] = backend; } } } @@ -362,7 +366,7 @@ QStringList QCalendarRegistry::availableCalendars() ensurePopulated(); QReadLocker locker(&lock); - return QStringList(byName.keyBegin(), byName.keyEnd()); + return byName.keys(); } /* @@ -378,9 +382,8 @@ const QCalendarBackend *QCalendarRegistry::fromName(QAnyStringView name) { ensurePopulated(); - const QString nameU16 = name.toString(); QReadLocker locker(&lock); - return byName.value(nameU16, nullptr); + return byName.value(name, nullptr); } /* @@ -450,12 +453,9 @@ QStringList QCalendarRegistry::backendNames(const QCalendarBackend *backend) QStringList l; l.reserve(byName.size()); // too large, but never really large, so ok - // same as byName.keys(backend), except for - // - the missing const on mapped_type and - // - CalendarName != QString as the key_type - for (auto it = byName.cbegin(), end = byName.cend(); it != end; ++it) { - if (it.value() == backend) - l.push_back(it.key()); + for (const auto &[key, value] : byName) { + if (value == backend) + l.push_back(key); } return l; |