diff --git a/autotests/udsentrytest.h b/autotests/udsentrytest.h --- a/autotests/udsentrytest.h +++ b/autotests/udsentrytest.h @@ -30,6 +30,7 @@ void testSaveLoad(); void testMove(); void testEquality(); + void testInitListConstructor(); }; #endif diff --git a/autotests/udsentrytest.cpp b/autotests/udsentrytest.cpp --- a/autotests/udsentrytest.cpp +++ b/autotests/udsentrytest.cpp @@ -331,4 +331,26 @@ QVERIFY(!(entry2 != entry3)); } +/** + * Test to verify that initialization list constructor works + */ +void UDSEntryTest::testInitListConstructor() +{ + KIO::UDSEntry entry { + {KIO::UDSEntry::UDS_SIZE, 1}, + {KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR}, + {KIO::UDSEntry::UDS_NAME, QStringLiteral("filename1")}, + {KIO::UDSEntry::UDS_MODIFICATION_TIME, (long long)123456}, + {KIO::UDSEntry::UDS_CREATION_TIME, (unsigned long long)12345}, + }; + + QCOMPARE(entry.count(), 5); + + QCOMPARE(entry.numberValue(KIO::UDSEntry::UDS_SIZE), 1); + QCOMPARE(entry.numberValue(KIO::UDSEntry::UDS_FILE_TYPE), S_IFDIR); + QCOMPARE(entry.stringValue(KIO::UDSEntry::UDS_NAME), QStringLiteral("filename1")); + QCOMPARE(entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME), (long long)123456); + QCOMPARE(entry.numberValue(KIO::UDSEntry::UDS_CREATION_TIME), (unsigned long long)12345); +} + QTEST_MAIN(UDSEntryTest) diff --git a/src/core/slavebase.cpp b/src/core/slavebase.cpp --- a/src/core/slavebase.cpp +++ b/src/core/slavebase.cpp @@ -546,12 +546,12 @@ if (!d->pendingListEntries.isEmpty()) { if (!d->m_rootEntryListed) { qCWarning(KIO_CORE) << "UDSEntry for '.' not found, creating a default one. Please fix the" << QCoreApplication::applicationName() << "KIO slave"; - KIO::UDSEntry entry; - entry.reserve(4); - entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); - entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); - entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0); - entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); + const KIO::UDSEntry entry { + {KIO::UDSEntry::UDS_NAME, QStringLiteral(".")}, + {KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR}, + {KIO::UDSEntry::UDS_SIZE, 0}, + {KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH}, + }; d->pendingListEntries.append(entry); } diff --git a/src/core/udsentry.h b/src/core/udsentry.h --- a/src/core/udsentry.h +++ b/src/core/udsentry.h @@ -30,6 +30,8 @@ #include "kiocore_export.h" +#include + namespace KIO { class UDSEntry; @@ -59,6 +61,82 @@ namespace KIO { class UDSEntryPrivate; + +/** + * @class KIO::UDSEntryInitListEntry udsentry.h + * + * Helper class for the @c std::initializer_list constructor of UDSEntry + * + * @since 5.70 + */ +class UDSEntryInitListEntry +{ + friend class UDSEntryPrivate; +public: + /** + * Constructor overload to handle string entries + */ + UDSEntryInitListEntry(uint field, const QString &value) + : m_field(field) + , m_isString(true) + { + new(m_u.s) QString(value); + } + /** + * Constructor overload to handle common numeric literals, like @c S_IFDIR + */ + UDSEntryInitListEntry(uint field, int l) + : m_u(l) + , m_field(field) + , m_isString(false) + { + } + /** + * Constructor overload to handle all signed integer values + */ + UDSEntryInitListEntry(uint field, long long l) + : m_u(l) + , m_field(field) + , m_isString(false) + { + } + /** + * Constructor overload to handle all unsigned integer values + */ + UDSEntryInitListEntry(uint field, unsigned long long l) + : m_u(l) + , m_field(field) + , m_isString(false) + { + } + ~UDSEntryInitListEntry() + { + if (m_isString) { + QString& string = *(reinterpret_cast(m_u.s)); + string.~QString(); + } + } + +private: + void insert(UDSEntryPrivate* entry) const; + +private: + Q_DISABLE_COPY(UDSEntryInitListEntry) + + // holds either the numeric or the string value + // done to avoid unneeded QString constructor & destructor in case of numeric value + union U { + U(long long l) : l(l) {} + U() {} + const long long l; + char s[sizeof(QString)]; + } m_u; + + const uint m_field; + const bool m_isString; +}; + + /** * @class KIO::UDSEntry udsentry.h * @@ -99,6 +177,24 @@ */ UDSEntry(const QT_STATBUF &buff, const QString &name = QString()); + /** + * Create a UDSEntry from an initialization list + * @param list list of entries + * + * Sample usage: + * @code + * KIO::UDSEntry entry { + * {KIO::UDSEntry::UDS_NAME, dirname}, + * {KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR}, + * {KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")}, + * {KIO::UDSEntry::UDS_MODIFICATION_TIME, mtime.toMSecsSinceEpoch() / 1000}, + * }; + * @endcode + * + * @since 5.70 + */ + UDSEntry(std::initializer_list list); + /** * Copy constructor */ diff --git a/src/core/udsentry.cpp b/src/core/udsentry.cpp --- a/src/core/udsentry.cpp +++ b/src/core/udsentry.cpp @@ -38,6 +38,9 @@ class KIO::UDSEntryPrivate : public QSharedData { public: + UDSEntryPrivate() = default; + UDSEntryPrivate(std::initializer_list list); + void reserve(int size); void insert(uint udsField, const QString &value); void replace(uint udsField, const QString &value); @@ -73,6 +76,14 @@ std::vector storage; }; +UDSEntryPrivate::UDSEntryPrivate(std::initializer_list list) +{ + reserve(int(list.size())); + for (auto& entry : list) { + entry.insert(this); + } +} + void UDSEntryPrivate::reserve(int size) { storage.reserve(size); @@ -321,14 +332,33 @@ } //END UDSEntryPrivate +//BEGIN UDSEntryInitListEntry +/* ---------- UDSEntryInitListEntry ------------ */ + +void UDSEntryInitListEntry::insert(UDSEntryPrivate* entry) const +{ + if (m_isString) { + entry->insert(m_field, *(reinterpret_cast(m_u.s))); + } else { + entry->insert(m_field, m_u.l); + } +} + +//END UDSEntryInitListEntry + //BEGIN UDSEntry /* ---------- UDSEntry ------------ */ UDSEntry::UDSEntry() : d(new UDSEntryPrivate()) { } +UDSEntry::UDSEntry(std::initializer_list list) + : d(new UDSEntryPrivate(list)) +{ +} + // BUG: this API doesn't allow to handle symlinks correctly (we need buff from QT_LSTAT for most things, but buff from QT_STAT for st_mode and st_size) UDSEntry::UDSEntry(const QT_STATBUF &buff, const QString &name) : d(new UDSEntryPrivate()) diff --git a/src/ioslaves/ftp/ftp.cpp b/src/ioslaves/ftp/ftp.cpp --- a/src/ioslaves/ftp/ftp.cpp +++ b/src/ioslaves/ftp/ftp.cpp @@ -1370,16 +1370,16 @@ // We can't stat root, but we know it's a dir. if (path.isEmpty() || path == QLatin1String("/")) { - UDSEntry entry; - entry.reserve(6); - //entry.insert( KIO::UDSEntry::UDS_NAME, UDSField( QString() ) ); - entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); - entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); - entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); - entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - entry.fastInsert(KIO::UDSEntry::UDS_USER, QStringLiteral("root")); - entry.fastInsert(KIO::UDSEntry::UDS_GROUP, QStringLiteral("root")); + const UDSEntry entry { + // {KIO::UDSEntry::UDS_NAME, UDSField( QString() ) }, + {KIO::UDSEntry::UDS_NAME, QStringLiteral(".")}, + {KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR}, + {KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")}, + {KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH}, + {KIO::UDSEntry::UDS_USER, QStringLiteral("root")}, + {KIO::UDSEntry::UDS_GROUP, QStringLiteral("root")}, // no size + }; q->statEntry(entry); return Result::pass(); @@ -1418,11 +1418,12 @@ // --- New implementation: // Don't list the parent dir. Too slow, might not show it, etc. // Just return that it's a dir. - UDSEntry entry; - entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); - entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); - entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + const UDSEntry entry { + {KIO::UDSEntry::UDS_NAME, filename}, + {KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR}, + {KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH}, // No clue about size, ownership, group, etc. + }; q->statEntry(entry); return Result::pass(); diff --git a/src/ioslaves/http/http.cpp b/src/ioslaves/http/http.cpp --- a/src/ioslaves/http/http.cpp +++ b/src/ioslaves/http/http.cpp @@ -718,11 +718,11 @@ } // When downloading we assume it exists - UDSEntry entry; - entry.reserve(3); - entry.fastInsert(KIO::UDSEntry::UDS_NAME, url.fileName()); - entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG); // a file - entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH); // readable by everybody + const UDSEntry entry { + {KIO::UDSEntry::UDS_NAME, url.fileName()}, + {KIO::UDSEntry::UDS_FILE_TYPE, S_IFREG}, // a file + {KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IRGRP | S_IROTH}, // readable by everybody + }; statEntry(entry); finished(); diff --git a/src/widgets/renamedialog.cpp b/src/widgets/renamedialog.cpp --- a/src/widgets/renamedialog.cpp +++ b/src/widgets/renamedialog.cpp @@ -217,28 +217,24 @@ if (d->src.isLocalFile()) { d->srcItem = KFileItem(d->src); } else { - UDSEntry srcUds; - - srcUds.reserve(4); - srcUds.fastInsert(UDSEntry::UDS_NAME, d->src.fileName()); - srcUds.fastInsert(UDSEntry::UDS_MODIFICATION_TIME, mtimeSrc.toMSecsSinceEpoch() / 1000); - srcUds.fastInsert(UDSEntry::UDS_CREATION_TIME, ctimeSrc.toMSecsSinceEpoch() / 1000); - srcUds.fastInsert(UDSEntry::UDS_SIZE, sizeSrc); - + const UDSEntry srcUds { + {UDSEntry::UDS_NAME, d->src.fileName()}, + {UDSEntry::UDS_MODIFICATION_TIME, mtimeSrc.toMSecsSinceEpoch() / 1000}, + {UDSEntry::UDS_CREATION_TIME, ctimeSrc.toMSecsSinceEpoch() / 1000}, + {UDSEntry::UDS_SIZE, sizeSrc}, + }; d->srcItem = KFileItem(srcUds, d->src); } if (d->dest.isLocalFile()) { d->destItem = KFileItem(d->dest); } else { - UDSEntry destUds; - - destUds.reserve(4); - destUds.fastInsert(UDSEntry::UDS_NAME, d->dest.fileName()); - destUds.fastInsert(UDSEntry::UDS_MODIFICATION_TIME, mtimeDest.toMSecsSinceEpoch() / 1000); - destUds.fastInsert(UDSEntry::UDS_CREATION_TIME, ctimeDest.toMSecsSinceEpoch() / 1000); - destUds.fastInsert(UDSEntry::UDS_SIZE, sizeDest); - + const UDSEntry destUds { + {UDSEntry::UDS_NAME, d->dest.fileName()}, + {UDSEntry::UDS_MODIFICATION_TIME, mtimeDest.toMSecsSinceEpoch() / 1000}, + {UDSEntry::UDS_CREATION_TIME, ctimeDest.toMSecsSinceEpoch() / 1000}, + {UDSEntry::UDS_SIZE, sizeDest}, + }; d->destItem = KFileItem(destUds, d->dest); } diff --git a/tests/udsentrybenchmark.cpp b/tests/udsentrybenchmark.cpp --- a/tests/udsentrybenchmark.cpp +++ b/tests/udsentrybenchmark.cpp @@ -58,6 +58,7 @@ UDSEntryBenchmark(); private Q_SLOTS: + void createSmallEntriesFromInitList(); void createSmallEntries(); void createLargeEntries(); void readFieldsFromSmallEntries(); @@ -112,6 +113,38 @@ } } +void UDSEntryBenchmark::createSmallEntriesFromInitList() +{ + m_smallEntries.clear(); + m_smallEntries.reserve(numberOfSmallUDSEntries); + + const QString user = QStringLiteral("user"); + const QString group = QStringLiteral("group"); + + QVector names(numberOfSmallUDSEntries); + for (int i = 0; i < numberOfSmallUDSEntries; ++i) { + names[i] = QString::number(i); + } + + QBENCHMARK_ONCE { + for (int i = 0; i < numberOfSmallUDSEntries; ++i) { + KIO::UDSEntry entry { + {KIO::UDSEntry::UDS_NAME, names[i]}, + {KIO::UDSEntry::UDS_FILE_TYPE, i}, + {KIO::UDSEntry::UDS_ACCESS, i}, + {KIO::UDSEntry::UDS_SIZE, i}, + {KIO::UDSEntry::UDS_MODIFICATION_TIME, i}, + {KIO::UDSEntry::UDS_USER, user}, + {KIO::UDSEntry::UDS_GROUP, group}, + {KIO::UDSEntry::UDS_ACCESS_TIME, i}, + }; + m_smallEntries.append(entry); + } + } + + Q_ASSERT(m_smallEntries.count() == numberOfSmallUDSEntries); +} + void UDSEntryBenchmark::createSmallEntries() { m_smallEntries.clear();