diff --git a/src/core/kfileitem.cpp b/src/core/kfileitem.cpp --- a/src/core/kfileitem.cpp +++ b/src/core/kfileitem.cpp @@ -196,8 +196,9 @@ const QString path = m_url.adjusted(QUrl::StripTrailingSlash).toLocalFile(); const QByteArray pathBA = QFile::encodeName(path); if (QT_LSTAT(pathBA.constData(), &buf) == 0) { - m_entry.insert(KIO::UDSEntry::UDS_DEVICE_ID, buf.st_dev); - m_entry.insert(KIO::UDSEntry::UDS_INODE, buf.st_ino); + m_entry.reserve(9); + m_entry.replace(KIO::UDSEntry::UDS_DEVICE_ID, buf.st_dev); + m_entry.replace(KIO::UDSEntry::UDS_INODE, buf.st_ino); mode_t mode = buf.st_mode; if ((buf.st_mode & QT_STAT_MASK) == QT_STAT_LNK) { @@ -208,14 +209,14 @@ mode = (QT_STAT_MASK - 1) | S_IRWXU | S_IRWXG | S_IRWXO; } } - m_entry.insert(KIO::UDSEntry::UDS_SIZE, buf.st_size); - m_entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, buf.st_mode & QT_STAT_MASK); // extract file type - m_entry.insert(KIO::UDSEntry::UDS_ACCESS, buf.st_mode & 07777); // extract permissions - m_entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, buf.st_mtime); // TODO: we could use msecs too... - m_entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, buf.st_atime); + m_entry.replace(KIO::UDSEntry::UDS_SIZE, buf.st_size); + m_entry.replace(KIO::UDSEntry::UDS_FILE_TYPE, buf.st_mode & QT_STAT_MASK); // extract file type + m_entry.replace(KIO::UDSEntry::UDS_ACCESS, buf.st_mode & 07777); // extract permissions + m_entry.replace(KIO::UDSEntry::UDS_MODIFICATION_TIME, buf.st_mtime); // TODO: we could use msecs too... + m_entry.replace(KIO::UDSEntry::UDS_ACCESS_TIME, buf.st_atime); #ifndef Q_OS_WIN - m_entry.insert(KIO::UDSEntry::UDS_USER, KUser(buf.st_uid).loginName()); - m_entry.insert(KIO::UDSEntry::UDS_GROUP, KUserGroup(buf.st_gid).name()); + m_entry.replace(KIO::UDSEntry::UDS_USER, KUser(buf.st_uid).loginName()); + m_entry.replace(KIO::UDSEntry::UDS_GROUP, KUserGroup(buf.st_gid).name()); #endif // TODO: these can be removed, we can use UDS_FILE_TYPE and UDS_ACCESS everywhere @@ -303,7 +304,7 @@ void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, uint time_t_val) const { - m_entry.insert(udsFieldForTime(mappedWhich), time_t_val); + m_entry.replace(udsFieldForTime(mappedWhich), time_t_val); } void KFileItemPrivate::setTime(KFileItem::FileTimes mappedWhich, const QDateTime &val) const @@ -547,7 +548,7 @@ return; } - d->m_entry.insert(KIO::UDSEntry::UDS_LOCAL_PATH, path); + d->m_entry.replace(KIO::UDSEntry::UDS_LOCAL_PATH, path); } void KFileItem::setName(const QString &name) @@ -562,7 +563,7 @@ d->m_strText = KIO::decodeFileName(d->m_strName); } if (d->m_entry.contains(KIO::UDSEntry::UDS_NAME)) { - d->m_entry.insert(KIO::UDSEntry::UDS_NAME, d->m_strName); // #195385 + d->m_entry.replace(KIO::UDSEntry::UDS_NAME, d->m_strName); // #195385 } } diff --git a/src/core/listjob.cpp b/src/core/listjob.cpp --- a/src/core/listjob.cpp +++ b/src/core/listjob.cpp @@ -172,8 +172,8 @@ if ((m_prefix.isNull() || (filename != QLatin1String("..") && filename != QLatin1String("."))) && (includeHidden || (filename[0] != '.'))) { // ## Didn't find a way to use the iterator instead of re-doing a key lookup - newone.insert(KIO::UDSEntry::UDS_NAME, m_prefix + filename); - newone.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, m_displayPrefix + displayName); + newone.replace(KIO::UDSEntry::UDS_NAME, m_prefix + filename); + newone.replace(KIO::UDSEntry::UDS_DISPLAY_NAME, m_displayPrefix + displayName); newlist.append(newone); } } diff --git a/src/core/udsentry.h b/src/core/udsentry.h --- a/src/core/udsentry.h +++ b/src/core/udsentry.h @@ -35,7 +35,14 @@ class UDSEntry; } -void debugUDSEntry(QDebug stream, const KIO::UDSEntry &entry); +KIOCORE_EXPORT QDataStream &operator<< (QDataStream &s, const KIO::UDSEntry &a); +KIOCORE_EXPORT QDataStream &operator>> (QDataStream &s, KIO::UDSEntry &a); + +/** + * Support for qDebug() << aUDSEntry + * \since 5.22 + */ +KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KIO::UDSEntry &entry); namespace KIO { @@ -136,13 +143,15 @@ * insert field with string value * @param field numeric field id * @param value to set + * @note since 5.47, it will assert if the field is already inserted. In that case, use replace() instead. */ void insert(uint field, const QString &value); /** * insert field with numeric value * @param field numeric field id * @param l value to set + * @note since 5.47, it will assert if the field is already inserted. In that case, use replace() instead. */ void insert(uint field, long long l); @@ -308,9 +317,26 @@ }; private: - friend class UDSEntryPrivate; - friend void ::debugUDSEntry(QDebug stream, const KIO::UDSEntry &entry); QSharedDataPointer d; + friend KIOCORE_EXPORT QDataStream& ::operator<< (QDataStream &s, const KIO::UDSEntry &a); + friend KIOCORE_EXPORT QDataStream& ::operator>> (QDataStream &s, KIO::UDSEntry &a); + friend KIOCORE_EXPORT QDebug (::operator<<) (QDebug stream, const KIO::UDSEntry &entry); +public: + /** + * Replace or insert field with string value + * @param field numeric field id + * @param value to set + * @since 5.47 + */ + void replace(uint field, const QString &value); + + /** + * Replace or insert field with numeric value + * @param field numeric field id + * @param l value to set + * @since 5.47 + */ + void replace(uint field, long long l); }; } @@ -339,15 +365,6 @@ typedef QList UDSEntryList; } // end namespace -KIOCORE_EXPORT QDataStream &operator<< (QDataStream &s, const KIO::UDSEntry &a); -KIOCORE_EXPORT QDataStream &operator>> (QDataStream &s, KIO::UDSEntry &a); - -/** - * Support for qDebug() << aUDSEntry - * \since 5.22 - */ -KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KIO::UDSEntry &entry); - Q_DECLARE_METATYPE(KIO::UDSEntry) #endif /*UDSENTRY_H*/ diff --git a/src/core/udsentry.cpp b/src/core/udsentry.cpp --- a/src/core/udsentry.cpp +++ b/src/core/udsentry.cpp @@ -32,40 +32,302 @@ using namespace KIO; -/* ---------- UDSEntry ------------ */ +//BEGIN UDSEntryPrivate +/* ---------- UDSEntryPrivate ------------ */ class KIO::UDSEntryPrivate : public QSharedData { public: - struct Field { - inline Field(const QString &value) : m_str(value), m_long(0) {} - inline Field(long long value = 0) : m_long(value) { } + void reserve(int size); + void insert(uint udsField, const QString &value); + void replace(uint udsField, const QString &value); + void insert(uint udsField, long long value); + void replace(uint udsField, long long value); + int count() const; + QString stringValue(uint udsField) const; + long long numberValue(uint udsField, long long defaultValue = -1) const; +#ifndef KIOCORE_NO_DEPRECATED + QList listFields() const; +#endif + QVector fields() const; + bool contains(uint udsField) const; + void clear(); + void save(QDataStream &s) const; + void load(QDataStream &s); + void debugUDSEntry(QDebug &stream) const; + /** + * @param field numeric UDS field id + * @return the name of the field + */ + static QString nameOfUdsField(uint field); + +private: + struct Field + { + inline Field() {} + inline Field(const uint index, const QString &value) : m_str(value), m_index(index) {} + inline Field(const uint index, long long value = 0) : m_long(value), m_index(index) {} + QString m_str; - long long m_long; + long long m_long = LLONG_MIN; + uint m_index = 0; }; + std::vector storage; +}; + +void UDSEntryPrivate::reserve(int size) +{ + storage.reserve(size); +} + +void UDSEntryPrivate::insert(uint udsField, const QString &value) +{ + Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING); + Q_ASSERT(std::find_if(storage.cbegin(), storage.cend(), + [udsField](const Field &entry) {return entry.m_index == udsField;}) == storage.cend()); + storage.emplace_back(udsField, value); +} - QVector fields; +void UDSEntryPrivate::replace(uint udsField, const QString &value) +{ + Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING); + auto it = std::find_if(storage.begin(), storage.end(), + [udsField](const Field &entry) {return entry.m_index == udsField;}); + if (it != storage.end()) { + it->m_str = value; + return; + } + storage.emplace_back(udsField, value); +} + +void UDSEntryPrivate::insert(uint udsField, long long value) +{ + Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER); + Q_ASSERT(std::find_if(storage.cbegin(), storage.cend(), + [udsField](const Field &entry) {return entry.m_index == udsField;}) == storage.cend()); + storage.emplace_back(udsField, value); +} - // If udsIndexes[i] == uds, then fields[i] contains the value for 'uds'. Example: - // udsIndexes = {UDS_NAME, UDS_FILE_SIZE, ...} - // fields = {Field("filename"), Field(1234), ...} - QVector udsIndexes; +void UDSEntryPrivate::replace(uint udsField, long long value) +{ + Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER); + auto it = std::find_if(storage.begin(), storage.end(), + [udsField](const Field &entry) {return entry.m_index == udsField;}); + if (it != storage.end()) { + it->m_long = value; + return; + } + storage.emplace_back(udsField, value); +} + +int UDSEntryPrivate::count() const +{ + return storage.size(); +} - void insert(uint uds, const Field& field) +QString UDSEntryPrivate::stringValue(uint udsField) const +{ + auto it = std::find_if(storage.cbegin(), storage.cend(), + [udsField](const Field &entry) {return entry.m_index == udsField;}); + if (it != storage.cend()) { + return it->m_str; + } + return QString(); +} + +long long UDSEntryPrivate::numberValue(uint udsField, long long defaultValue) const +{ + auto it = std::find_if(storage.cbegin(), storage.cend(), + [udsField](const Field &entry) {return entry.m_index == udsField;}); + if (it != storage.cend()) { + return it->m_long; + } + return defaultValue; +} + +#ifndef KIOCORE_NO_DEPRECATED +QList UDSEntryPrivate::listFields() const +{ + QList res; + res.reserve(storage.size()); + for (auto it = storage.cbegin(), end = storage.cend(); it != end; ++it) { + res.append(it->m_index); + } + return res; +} +#endif + +QVector UDSEntryPrivate::fields() const +{ + QVector res; + res.reserve(storage.size()); + for (auto it = storage.cbegin(), end = storage.cend(); it != end; ++it) { + res.append(it->m_index); + } + return res; +} + +bool UDSEntryPrivate::contains(uint udsField) const +{ + auto it = std::find_if(storage.cbegin(), storage.cend(), + [udsField](const Field &entry) {return entry.m_index == udsField;}); + return (it != storage.cend()); +} + +void UDSEntryPrivate::clear() +{ + storage.clear(); +} + +void UDSEntryPrivate::save(QDataStream &s) const +{ + s << static_cast(storage.size()); + + for (auto it = storage.cbegin(), end = storage.cend(); it != end; ++it) { - const int index = udsIndexes.indexOf(uds); - if (index >= 0) { - fields[index] = field; + uint uds = it->m_index; + s << uds; + + if (uds & KIO::UDSEntry::UDS_STRING) { + s << it->m_str; + } else if (uds & KIO::UDSEntry::UDS_NUMBER) { + s << it->m_long; } else { - udsIndexes.append(uds); - fields.append(field); + Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); } } +} - static void save(QDataStream &, const UDSEntry &); - static void load(QDataStream &, UDSEntry &); -}; -Q_DECLARE_TYPEINFO(KIO::UDSEntryPrivate::Field, Q_MOVABLE_TYPE); +void UDSEntryPrivate::load(QDataStream &s) +{ + clear(); + + quint32 size; + s >> size; + reserve(size); + + // We cache the loaded strings. Some of them, like, e.g., the user, + // will often be the same for many entries in a row. Caching them + // permits to use implicit sharing to save memory. + static QVector cachedStrings; + if (quint32(cachedStrings.size()) < size) { + cachedStrings.resize(size); + } + + for (quint32 i = 0; i < size; ++i) { + quint32 uds; + s >> uds; + + if (uds & KIO::UDSEntry::UDS_STRING) { + // If the QString is the same like the one we read for the + // previous UDSEntry at the i-th position, use an implicitly + // shared copy of the same QString to save memory. + QString buffer; + s >> buffer; + + if (buffer != cachedStrings.at(i)) { + cachedStrings[i] = buffer; + } + + insert(uds, cachedStrings.at(i)); + } else if (uds & KIO::UDSEntry::UDS_NUMBER) { + long long value; + s >> value; + insert(uds, value); + } else { + Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); + } + } +} + +QString UDSEntryPrivate::nameOfUdsField(uint field) +{ + switch (field) { + case UDSEntry::UDS_SIZE: + return QStringLiteral("UDS_SIZE"); + case UDSEntry::UDS_SIZE_LARGE: + return QStringLiteral("UDS_SIZE_LARGE"); + case UDSEntry::UDS_USER: + return QStringLiteral("UDS_USER"); + case UDSEntry::UDS_ICON_NAME: + return QStringLiteral("UDS_ICON_NAME"); + case UDSEntry::UDS_GROUP: + return QStringLiteral("UDS_GROUP"); + case UDSEntry::UDS_NAME: + return QStringLiteral("UDS_NAME"); + case UDSEntry::UDS_LOCAL_PATH: + return QStringLiteral("UDS_LOCAL_PATH"); + case UDSEntry::UDS_HIDDEN: + return QStringLiteral("UDS_HIDDEN"); + case UDSEntry::UDS_ACCESS: + return QStringLiteral("UDS_ACCESS"); + case UDSEntry::UDS_MODIFICATION_TIME: + return QStringLiteral("UDS_MODIFICATION_TIME"); + case UDSEntry::UDS_ACCESS_TIME: + return QStringLiteral("UDS_ACCESS_TIME"); + case UDSEntry::UDS_CREATION_TIME: + return QStringLiteral("UDS_CREATION_TIME"); + case UDSEntry::UDS_FILE_TYPE: + return QStringLiteral("UDS_FILE_TYPE"); + case UDSEntry::UDS_LINK_DEST: + return QStringLiteral("UDS_LINK_DEST"); + case UDSEntry::UDS_URL: + return QStringLiteral("UDS_URL"); + case UDSEntry::UDS_MIME_TYPE: + return QStringLiteral("UDS_MIME_TYPE"); + case UDSEntry::UDS_GUESSED_MIME_TYPE: + return QStringLiteral("UDS_GUESSED_MIME_TYPE"); + case UDSEntry::UDS_XML_PROPERTIES: + return QStringLiteral("UDS_XML_PROPERTIES"); + case UDSEntry::UDS_EXTENDED_ACL: + return QStringLiteral("UDS_EXTENDED_ACL"); + case UDSEntry::UDS_ACL_STRING: + return QStringLiteral("UDS_ACL_STRING"); + case UDSEntry::UDS_DEFAULT_ACL_STRING: + return QStringLiteral("UDS_DEFAULT_ACL_STRING"); + case UDSEntry::UDS_DISPLAY_NAME: + return QStringLiteral("UDS_DISPLAY_NAME"); + case UDSEntry::UDS_TARGET_URL: + return QStringLiteral("UDS_TARGET_URL"); + case UDSEntry::UDS_DISPLAY_TYPE: + return QStringLiteral("UDS_DISPLAY_TYPE"); + case UDSEntry::UDS_ICON_OVERLAY_NAMES: + return QStringLiteral("UDS_ICON_OVERLAY_NAMES"); + case UDSEntry::UDS_COMMENT: + return QStringLiteral("UDS_COMMENT"); + case UDSEntry::UDS_DEVICE_ID: + return QStringLiteral("UDS_DEVICE_ID"); + case UDSEntry::UDS_INODE: + return QStringLiteral("UDS_INODE"); + case UDSEntry::UDS_EXTRA: + return QStringLiteral("UDS_EXTRA"); + case UDSEntry::UDS_EXTRA_END: + return QStringLiteral("UDS_EXTRA_END"); + default: + return QString("Unknown uds field %1").arg(field); + } +} + +void UDSEntryPrivate::debugUDSEntry(QDebug &stream) const +{ + QDebugStateSaver saver(stream); + stream.nospace() << "["; + for (auto it = storage.cbegin(), end = storage.cend(); it != end; ++it) { + stream << " " << nameOfUdsField(it->m_index) << "="; + if (it->m_index & KIO::UDSEntry::UDS_STRING) { + stream << it->m_str; + } else if (it->m_index & KIO::UDSEntry::UDS_NUMBER) { + stream << it->m_long; + } else { + Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); + } + } + stream << " ]"; +} +//END UDSEntryPrivate + +//BEGIN UDSEntry +/* ---------- UDSEntry ------------ */ UDSEntry::UDSEntry() : d(new UDSEntryPrivate()) @@ -76,7 +338,11 @@ UDSEntry::UDSEntry(const QT_STATBUF &buff, const QString &name) : d(new UDSEntryPrivate()) { - reserve(9); +#ifndef Q_OS_WIN + reserve(10); +#else + reserve(8); +#endif insert(UDS_NAME, name); insert(UDS_SIZE, buff.st_size); insert(UDS_DEVICE_ID, buff.st_dev); @@ -99,22 +365,12 @@ QString UDSEntry::stringValue(uint field) const { - const int index = d->udsIndexes.indexOf(field); - if (index >= 0) { - return d->fields.at(index).m_str; - } else { - return QString(); - } + return d->stringValue(field); } long long UDSEntry::numberValue(uint field, long long defaultValue) const { - const int index = d->udsIndexes.indexOf(field); - if (index >= 0) { - return d->fields.at(index).m_long; - } else { - return defaultValue; - } + return d->numberValue(field, defaultValue); } bool UDSEntry::isDir() const @@ -129,152 +385,71 @@ void UDSEntry::reserve(int size) { - d->fields.reserve(size); - d->udsIndexes.reserve(size); + d->reserve(size); } void UDSEntry::insert(uint field, const QString &value) { - d->insert(field, UDSEntryPrivate::Field(value)); + d->insert(field, value); } void UDSEntry::insert(uint field, long long value) { - d->insert(field, UDSEntryPrivate::Field(value)); + d->insert(field, value); +} + +void UDSEntry::replace(uint field, const QString &value) +{ + d->replace(field, value); +} + +void UDSEntry::replace(uint field, long long value) +{ + d->replace(field, value); } #ifndef KIOCORE_NO_DEPRECATED QList UDSEntry::listFields() const { - return d->udsIndexes.toList(); + return d->listFields(); } #endif QVector UDSEntry::fields() const { - return d->udsIndexes; + return d->fields(); } int UDSEntry::count() const { - return d->udsIndexes.count(); + return d->count(); } bool UDSEntry::contains(uint field) const { - return d->udsIndexes.contains(field); + return d->contains(field); } void UDSEntry::clear() { - d->fields.clear(); - d->udsIndexes.clear(); -} - -QDataStream &operator<<(QDataStream &s, const UDSEntry &a) -{ - UDSEntryPrivate::save(s, a); - return s; -} - -QDataStream &operator>>(QDataStream &s, UDSEntry &a) -{ - UDSEntryPrivate::load(s, a); - return s; -} - -void UDSEntryPrivate::save(QDataStream &s, const UDSEntry &a) -{ - const QVector &udsIndexes = a.d->udsIndexes; - const QVector &fields = a.d->fields; - const int size = udsIndexes.size(); - - s << size; - - for (int index = 0; index < size; ++index) { - uint uds = udsIndexes.at(index); - s << uds; - - if (uds & KIO::UDSEntry::UDS_STRING) { - s << fields.at(index).m_str; - } else if (uds & KIO::UDSEntry::UDS_NUMBER) { - s << fields.at(index).m_long; - } else { - Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); - } - } + d->clear(); } +//END UDSEntry KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KIO::UDSEntry &entry) { - debugUDSEntry(stream, entry); + entry.d->debugUDSEntry(stream); return stream; } -void UDSEntryPrivate::load(QDataStream &s, UDSEntry &a) +KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KIO::UDSEntry &a) { - a.clear(); - - QVector &fields = a.d->fields; - QVector &udsIndexes = a.d->udsIndexes; - - quint32 size; - s >> size; - fields.reserve(size); - udsIndexes.reserve(size); - - // We cache the loaded strings. Some of them, like, e.g., the user, - // will often be the same for many entries in a row. Caching them - // permits to use implicit sharing to save memory. - static QVector cachedStrings; - if (quint32(cachedStrings.size()) < size) { - cachedStrings.resize(size); - } - - for (quint32 i = 0; i < size; ++i) { - quint32 uds; - s >> uds; - udsIndexes.append(uds); - - if (uds & KIO::UDSEntry::UDS_STRING) { - // If the QString is the same like the one we read for the - // previous UDSEntry at the i-th position, use an implicitly - // shared copy of the same QString to save memory. - QString buffer; - s >> buffer; - - if (buffer != cachedStrings.at(i)) { - cachedStrings[i] = buffer; - } - - fields.append(Field(cachedStrings.at(i))); - } else if (uds & KIO::UDSEntry::UDS_NUMBER) { - long long value; - s >> value; - fields.append(Field(value)); - } else { - Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); - } - } + a.d->save(s); + return s; } -void debugUDSEntry(QDebug stream, const KIO::UDSEntry &entry) +KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, KIO::UDSEntry &a) { - const QVector &udsIndexes = entry.d->udsIndexes; - const QVector &fields = entry.d->fields; - const int size = udsIndexes.size(); - QDebugStateSaver saver(stream); - stream.nospace() << "["; - for (int index = 0; index < size; ++index) { - const uint uds = udsIndexes.at(index); - stream << " " << (uds & 0xffff) << "="; // we could use a switch statement for readability :-) - if (uds & KIO::UDSEntry::UDS_STRING) { - stream << fields.at(index).m_str; - } else if (uds & KIO::UDSEntry::UDS_NUMBER) { - stream << fields.at(index).m_long; - } else { - Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); - } - } - stream << " ]"; + a.d->load(s); + return s; }