summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2022-01-28 11:23:55 +0100
committerMarc Mutz <marc.mutz@qt.io>2022-01-30 14:18:36 +0000
commit2e4db2596a69fe9efd0d72a4da7d588716ad2a59 (patch)
tree6342370a25fbc7bb8dfbe822cc7f2318994b51a5
parent9135531c33551e03ef2b7287830228def0a81f5a (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.cpp56
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;