summaryrefslogtreecommitdiffstats
path: root/src/corelib/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/plugin')
-rw-r--r--src/corelib/plugin/qcoffpeparser.cpp2
-rw-r--r--src/corelib/plugin/qelfparser_p.cpp25
-rw-r--r--src/corelib/plugin/qfactoryloader.cpp278
-rw-r--r--src/corelib/plugin/qfactoryloader_p.h5
-rw-r--r--src/corelib/plugin/qlibrary.cpp123
-rw-r--r--src/corelib/plugin/qlibrary_p.h11
-rw-r--r--src/corelib/plugin/qlibrary_unix.cpp61
-rw-r--r--src/corelib/plugin/qmachparser.cpp25
-rw-r--r--src/corelib/plugin/qplugin.h2
-rw-r--r--src/corelib/plugin/qplugin.qdoc9
-rw-r--r--src/corelib/plugin/qpluginloader.cpp34
-rw-r--r--src/corelib/plugin/quuid.cpp257
-rw-r--r--src/corelib/plugin/quuid.h194
13 files changed, 768 insertions, 258 deletions
diff --git a/src/corelib/plugin/qcoffpeparser.cpp b/src/corelib/plugin/qcoffpeparser.cpp
index 054a433603..639e402a07 100644
--- a/src/corelib/plugin/qcoffpeparser.cpp
+++ b/src/corelib/plugin/qcoffpeparser.cpp
@@ -117,7 +117,7 @@ Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, HeaderDebug h)
switch (h.h->FileHeader.Machine) {
case IMAGE_FILE_MACHINE_I386: d << "i386"; break;
case IMAGE_FILE_MACHINE_ARM: d << "ARM"; break;
- case IMAGE_FILE_MACHINE_ARMNT: d << "ARM Thumb-2"; break;;
+ case IMAGE_FILE_MACHINE_ARMNT: d << "ARM Thumb-2"; break;
case IMAGE_FILE_MACHINE_THUMB: d << "Thumb"; break;
case IMAGE_FILE_MACHINE_IA64: d << "IA-64"; break;
case IMAGE_FILE_MACHINE_MIPS16: d << "MIPS16"; break;
diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp
index 4388c6babe..7f6271cde4 100644
--- a/src/corelib/plugin/qelfparser_p.cpp
+++ b/src/corelib/plugin/qelfparser_p.cpp
@@ -20,11 +20,6 @@
# error "Need ELF header to parse plugins."
#endif
-// Support older ELFOSABI define for GNU/Linux
-#if !defined(ELFOSABI_GNU) && defined(ELFOSABI_LINUX)
-# define ELFOSABI_GNU ELFOSABI_LINUX
-#endif
-
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -59,6 +54,10 @@ static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser")
# define PT_GNU_PROPERTY 0x6474e553
#endif
+#ifndef PN_XNUM
+# define PN_XNUM 0xffff
+#endif
+
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
@@ -112,6 +111,8 @@ struct ElfMachineCheck
static const Elf32_Half ExpectedMachine =
#if 0
// nothing
+#elif defined(Q_PROCESSOR_ALPHA)
+ EM_ALPHA
#elif defined(Q_PROCESSOR_ARM_32)
EM_ARM
#elif defined(Q_PROCESSOR_ARM_64)
@@ -122,6 +123,8 @@ struct ElfMachineCheck
EM_PARISC
#elif defined(Q_PROCESSOR_IA64)
EM_IA_64
+#elif defined(Q_PROCESSOR_LOONGARCH)
+ EM_LOONGARCH
#elif defined(Q_PROCESSOR_M68K)
EM_68K
#elif defined(Q_PROCESSOR_MIPS)
@@ -336,7 +339,7 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade
case ELFOSABI_SYSV: d << " (SYSV"; break;
case ELFOSABI_HPUX: d << " (HP-UX"; break;
case ELFOSABI_NETBSD: d << " (NetBSD"; break;
- case ELFOSABI_GNU: d << " (GNU/Linux"; break;
+ case ELFOSABI_LINUX: d << " (GNU/Linux"; break;
case ELFOSABI_SOLARIS: d << " (Solaris"; break;
case ELFOSABI_AIX: d << " (AIX"; break;
case ELFOSABI_IRIX: d << " (IRIX"; break;
@@ -380,6 +383,7 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade
switch (r.machine) {
// list definitely not exhaustive!
case EM_NONE: d << ", no machine"; break;
+ case EM_ALPHA: d << ", Alpha"; break;
case EM_68K: d << ", MC68000"; break;
case EM_ARM: d << ", ARM"; break;
case EM_AARCH64: d << ", AArch64"; break;
@@ -387,6 +391,9 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade
case EM_BLACKFIN: d << ", Blackfin"; break;
#endif
case EM_IA_64: d << ", IA-64"; break;
+#ifdef EM_LOONGARCH
+ case EM_LOONGARCH: d << ", LoongArch"; break;
+#endif
case EM_MIPS: d << ", MIPS"; break;
case EM_PARISC: d << ", HPPA"; break;
case EM_PPC: d << ", PowerPC"; break;
@@ -394,7 +401,9 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade
#ifdef EM_RISCV
case EM_RISCV: d << ", RISC-V"; break;
#endif
+#ifdef EM_S390
case EM_S390: d << ", S/390"; break;
+#endif
case EM_SH: d << ", SuperH"; break;
case EM_SPARC: d << ", SPARC"; break;
case EM_SPARCV9: d << ", SPARCv9"; break;
@@ -534,6 +543,8 @@ static bool preScanProgramHeaders(QByteArrayView data, const ErrorMaker &error)
// first, validate the extent of the full program header table
T::Word e_phnum = header->e_phnum;
+ if (e_phnum == PN_XNUM)
+ return error(QLibrary::tr("unimplemented: PN_XNUM program headers")), false;
T::Off offset = e_phnum * sizeof(T::Phdr); // can't overflow due to size of T::Half
if (qAddOverflow(offset, header->e_phoff, &offset) || offset > size_t(data.size()))
return error(QLibrary::tr("program header table extends past the end of the file")), false;
@@ -688,7 +699,7 @@ static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &er
// sections aren't allowed to extend past the end of the file, unless
// they are NOBITS sections
if (shdr->sh_type == SHT_NOBITS)
- continue;;
+ continue;
if (T::Off end; qAddOverflow(shdr->sh_offset, shdr->sh_size, &end)
|| end > size_t(data.size())) {
return error(QLibrary::tr("section contents extend past the end of the file"));
diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp
index a9d6247697..e2d9a40cb4 100644
--- a/src/corelib/plugin/qfactoryloader.cpp
+++ b/src/corelib/plugin/qfactoryloader.cpp
@@ -5,22 +5,19 @@
#include "qfactoryloader_p.h"
#ifndef QT_NO_QOBJECT
-#include "qfactoryinterface.h"
-
#include "private/qcoreapplication_p.h"
#include "private/qduplicatetracker_p.h"
#include "private/qloggingregistry_p.h"
#include "private/qobject_p.h"
#include "qcborarray.h"
#include "qcbormap.h"
+#include "qcborstreamreader.h"
#include "qcborvalue.h"
-#include "qcborvalue.h"
-#include "qdiriterator.h"
+#include "qdirlisting.h"
#include "qfileinfo.h"
#include "qjsonarray.h"
#include "qjsondocument.h"
#include "qjsonobject.h"
-#include "qmap.h"
#include "qmutex.h"
#include "qplugin.h"
#include "qplugin_p.h"
@@ -32,30 +29,189 @@
#include <qtcore_tracepoints_p.h>
+#include <map>
+#include <vector>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-bool QPluginParsedMetaData::parse(QByteArrayView raw)
+Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName);
+
+namespace {
+struct IterationResult
+{
+ enum Result {
+ FinishedSearch = 0,
+ ContinueSearch,
+
+ // parse errors
+ ParsingError = -1,
+ InvalidMetaDataVersion = -2,
+ InvalidTopLevelItem = -3,
+ InvalidHeaderItem = -4,
+ };
+ Result result;
+ QCborError error = { QCborError::NoError };
+
+ Q_IMPLICIT IterationResult(Result r) : result(r) {}
+ Q_IMPLICIT IterationResult(QCborError e) : result(ParsingError), error(e) {}
+};
+
+struct QFactoryLoaderIidSearch
+{
+ QLatin1StringView iid;
+ bool matchesIid = false;
+ QFactoryLoaderIidSearch(QLatin1StringView iid) : iid(iid)
+ { Q_ASSERT(!iid.isEmpty()); }
+
+ static IterationResult::Result skip(QCborStreamReader &reader)
+ {
+ // skip this, whatever it is
+ reader.next();
+ return IterationResult::ContinueSearch;
+ }
+
+ IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader)
+ {
+ if (key != QtPluginMetaDataKeys::IID)
+ return skip(reader);
+ matchesIid = (reader.readAllString() == iid);
+ return IterationResult::FinishedSearch;
+ }
+ IterationResult::Result operator()(QUtf8StringView, QCborStreamReader &reader)
+ {
+ return skip(reader);
+ }
+};
+
+struct QFactoryLoaderMetaDataKeysExtractor : QFactoryLoaderIidSearch
+{
+ QCborArray keys;
+ QFactoryLoaderMetaDataKeysExtractor(QLatin1StringView iid)
+ : QFactoryLoaderIidSearch(iid)
+ {}
+
+ IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader)
+ {
+ if (key == QtPluginMetaDataKeys::IID) {
+ QFactoryLoaderIidSearch::operator()(key, reader);
+ return IterationResult::ContinueSearch;
+ }
+ if (key != QtPluginMetaDataKeys::MetaData)
+ return skip(reader);
+
+ if (!matchesIid)
+ return IterationResult::FinishedSearch;
+ if (!reader.isMap() || !reader.isLengthKnown())
+ return IterationResult::InvalidHeaderItem;
+ if (!reader.enterContainer())
+ return IterationResult::ParsingError;
+ while (reader.isValid()) {
+ // the metadata is JSON, so keys are all strings
+ QByteArray key = reader.readAllUtf8String();
+ if (key == "Keys") {
+ if (!reader.isArray() || !reader.isLengthKnown())
+ return IterationResult::InvalidHeaderItem;
+ keys = QCborValue::fromCbor(reader).toArray();
+ break;
+ }
+ skip(reader);
+ }
+ // warning: we may not have finished iterating over the header
+ return IterationResult::FinishedSearch;
+ }
+ using QFactoryLoaderIidSearch::operator();
+};
+} // unnamed namespace
+
+template <typename F> static IterationResult iterateInPluginMetaData(QByteArrayView raw, F &&f)
{
QPluginMetaData::Header header;
Q_ASSERT(raw.size() >= qsizetype(sizeof(header)));
memcpy(&header, raw.data(), sizeof(header));
if (Q_UNLIKELY(header.version > QPluginMetaData::CurrentMetaDataVersion))
- return setError(QFactoryLoader::tr("Invalid metadata version"));
+ return IterationResult::InvalidMetaDataVersion;
// use fromRawData to keep QCborStreamReader from copying
raw = raw.sliced(sizeof(header));
QByteArray ba = QByteArray::fromRawData(raw.data(), raw.size());
- QCborParserError err;
- QCborValue metadata = QCborValue::fromCbor(ba, &err);
+ QCborStreamReader reader(ba);
+ if (reader.isInvalid())
+ return reader.lastError();
+ if (!reader.isMap())
+ return IterationResult::InvalidTopLevelItem;
+ if (!reader.enterContainer())
+ return reader.lastError();
+ while (reader.isValid()) {
+ IterationResult::Result r;
+ if (reader.isInteger()) {
+ // integer key, one of ours
+ qint64 value = reader.toInteger();
+ auto key = QtPluginMetaDataKeys(value);
+ if (qint64(key) != value)
+ return IterationResult::InvalidHeaderItem;
+ if (!reader.next())
+ return reader.lastError();
+ r = f(key, reader);
+ } else if (reader.isString()) {
+ QByteArray key = reader.readAllUtf8String();
+ if (key.isNull())
+ return reader.lastError();
+ r = f(QUtf8StringView(key), reader);
+ } else {
+ return IterationResult::InvalidTopLevelItem;
+ }
- if (err.error != QCborError::NoError)
- return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(err.error.toString()));
- if (!metadata.isMap())
+ if (QCborError e = reader.lastError())
+ return e;
+ if (r != IterationResult::ContinueSearch)
+ return r;
+ }
+
+ if (!reader.leaveContainer())
+ return reader.lastError();
+ return IterationResult::FinishedSearch;
+}
+
+static bool isIidMatch(QByteArrayView raw, QLatin1StringView iid)
+{
+ QFactoryLoaderIidSearch search(iid);
+ iterateInPluginMetaData(raw, search);
+ return search.matchesIid;
+}
+
+bool QPluginParsedMetaData::parse(QByteArrayView raw)
+{
+ QCborMap map;
+ auto r = iterateInPluginMetaData(raw, [&](const auto &key, QCborStreamReader &reader) {
+ QCborValue item = QCborValue::fromCbor(reader);
+ if (item.isInvalid())
+ return IterationResult::ParsingError;
+ if constexpr (std::is_enum_v<std::decay_t<decltype(key)>>)
+ map[int(key)] = item;
+ else
+ map[QString::fromUtf8(key)] = item;
+ return IterationResult::ContinueSearch;
+ });
+
+ switch (r.result) {
+ case IterationResult::FinishedSearch:
+ case IterationResult::ContinueSearch:
+ break;
+
+ // parse errors
+ case IterationResult::ParsingError:
+ return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(r.error.toString()));
+ case IterationResult::InvalidMetaDataVersion:
+ return setError(QFactoryLoader::tr("Invalid metadata version"));
+ case IterationResult::InvalidTopLevelItem:
+ case IterationResult::InvalidHeaderItem:
return setError(QFactoryLoader::tr("Unexpected metadata contents"));
- QCborMap map = metadata.toMap();
- metadata = {};
+ }
+
+ // header was validated
+ auto header = qFromUnaligned<QPluginMetaData::Header>(raw.data());
DecodedArchRequirements archReq =
header.version == 0 ? decodeVersion0ArchRequirements(header.plugin_arch_requirements)
@@ -96,6 +252,7 @@ QJsonObject QPluginParsedMetaData::toJson() const
class QFactoryLoaderPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QFactoryLoader)
+ Q_DISABLE_COPY_MOVE(QFactoryLoaderPrivate)
public:
QFactoryLoaderPrivate() { }
QByteArray iid;
@@ -103,8 +260,8 @@ public:
~QFactoryLoaderPrivate();
mutable QMutex mutex;
QDuplicateTracker<QString> loadedPaths;
- QList<QLibraryPrivate*> libraryList;
- QMap<QString,QLibraryPrivate*> keyMap;
+ std::vector<QLibraryPrivate::UniquePtr> libraries;
+ std::map<QString, QLibraryPrivate*> keyMap;
QString suffix;
QString extraSearchPath;
Qt::CaseSensitivity cs;
@@ -131,10 +288,7 @@ struct QFactoryLoaderGlobals
Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global)
QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
-{
- for (QLibraryPrivate *library : std::as_const(libraryList))
- library->release();
-}
+ = default;
inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
{
@@ -149,7 +303,7 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
qCDebug(lcFactoryLoader) << "checking directory path" << path << "...";
- QDirIterator plugins(path,
+ QDirListing plugins(path,
#if defined(Q_OS_WIN)
QStringList(QStringLiteral("*.dll")),
#elif defined(Q_OS_ANDROID)
@@ -157,9 +311,9 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
#endif
QDir::Files);
- while (plugins.hasNext()) {
- QString fileName = plugins.next();
-#ifdef Q_OS_MAC
+ for (const auto &dirEntry : plugins) {
+ const QString &fileName = dirEntry.fileName();
+#ifdef Q_OS_DARWIN
const bool isDebugPlugin = fileName.endsWith("_debug.dylib"_L1);
const bool isDebugLibrary =
#ifdef QT_DEBUG
@@ -182,8 +336,8 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
Q_TRACE(QFactoryLoader_update, fileName);
- std::unique_ptr<QLibraryPrivate, LibraryReleaser> library;
- library.reset(QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath()));
+ QLibraryPrivate::UniquePtr library;
+ library.reset(QLibraryPrivate::findOrCreate(dirEntry.canonicalFilePath()));
if (!library->isPlugin()) {
qCDebug(lcFactoryLoader) << library->errorString << Qt::endl
<< " not a plugin";
@@ -214,20 +368,20 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
// whereas the new one has a Qt version that fits
// better
constexpr int QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0);
- QLibraryPrivate *previous = keyMap.value(key);
+ QLibraryPrivate *&previous = keyMap[key];
int prev_qt_version = 0;
if (previous)
prev_qt_version = int(previous->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
int qt_version = int(library->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
if (!previous || (prev_qt_version > QtVersionNoPatch && qt_version <= QtVersionNoPatch)) {
- keyMap[key] = library.get(); // we WILL .release()
+ previous = library.get(); // we WILL .release()
++keyUsageCount;
}
}
if (keyUsageCount || keys.isEmpty()) {
library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
QMutexLocker locker(&mutex);
- libraryList += library.release();
+ libraries.push_back(std::move(library));
}
};
}
@@ -264,11 +418,14 @@ QFactoryLoader::~QFactoryLoader()
}
}
-#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC)
+#if defined(Q_OS_UNIX) && !defined (Q_OS_DARWIN)
QLibraryPrivate *QFactoryLoader::library(const QString &key) const
{
Q_D(const QFactoryLoader);
- return d->keyMap.value(d->cs ? key : key.toLower());
+ const auto it = d->keyMap.find(d->cs ? key : key.toLower());
+ if (it == d->keyMap.cend())
+ return nullptr;
+ return it->second;
}
#endif
@@ -326,7 +483,7 @@ void QFactoryLoader::setExtraSearchPath(const QString &path)
} else {
// must re-scan everything
d->loadedPaths.clear();
- d->libraryList.clear();
+ d->libraries.clear();
d->keyMap.clear();
update();
}
@@ -341,7 +498,7 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const
QList<QPluginParsedMetaData> metaData;
#if QT_CONFIG(library)
QMutexLocker locker(&d->mutex);
- for (QLibraryPrivate *library : std::as_const(d->libraryList))
+ for (const auto &library : d->libraries)
metaData.append(library->metaData);
#endif
@@ -354,6 +511,37 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const
continue;
metaData.append(std::move(parsed));
}
+
+ // other portions of the code will cast to int (e.g., keyMap())
+ Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max());
+ return metaData;
+}
+
+QList<QCborArray> QFactoryLoader::metaDataKeys() const
+{
+ Q_D(const QFactoryLoader);
+ QList<QCborArray> metaData;
+#if QT_CONFIG(library)
+ QMutexLocker locker(&d->mutex);
+ for (const auto &library : d->libraries) {
+ const QCborValue md = library->metaData.value(QtPluginMetaDataKeys::MetaData);
+ metaData.append(md["Keys"_L1].toArray());
+ }
+#endif
+
+ QLatin1StringView iid(d->iid.constData(), d->iid.size());
+ const auto staticPlugins = QPluginLoader::staticPlugins();
+ for (const QStaticPlugin &plugin : staticPlugins) {
+ QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData),
+ plugin.rawMetaDataSize);
+ QFactoryLoaderMetaDataKeysExtractor extractor{ iid };
+ iterateInPluginMetaData(pluginData, extractor);
+ if (extractor.matchesIid)
+ metaData += std::move(extractor.keys);
+ }
+
+ // other portions of the code will cast to int (e.g., keyMap())
+ Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max());
return metaData;
}
@@ -365,8 +553,8 @@ QObject *QFactoryLoader::instance(int index) const
#if QT_CONFIG(library)
QMutexLocker lock(&d->mutex);
- if (index < d->libraryList.size()) {
- QLibraryPrivate *library = d->libraryList.at(index);
+ if (size_t(index) < d->libraries.size()) {
+ QLibraryPrivate *library = d->libraries[index].get();
if (QObject *obj = library->pluginInstance()) {
if (!obj->parent())
obj->moveToThread(QCoreApplicationPrivate::mainThread());
@@ -374,7 +562,8 @@ QObject *QFactoryLoader::instance(int index) const
}
return nullptr;
}
- index -= d->libraryList.size();
+ // we know d->libraries.size() <= index <= numeric_limits<decltype(index)>::max() → no overflow
+ index -= static_cast<int>(d->libraries.size());
lock.unlock();
#endif
@@ -382,8 +571,7 @@ QObject *QFactoryLoader::instance(int index) const
const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
for (QStaticPlugin plugin : staticPlugins) {
QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize);
- QPluginParsedMetaData parsed(pluginData);
- if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid)
+ if (!isIidMatch(pluginData, iid))
continue;
if (index == 0)
@@ -397,10 +585,9 @@ QObject *QFactoryLoader::instance(int index) const
QMultiMap<int, QString> QFactoryLoader::keyMap() const
{
QMultiMap<int, QString> result;
- const QList<QPluginParsedMetaData> metaDataList = metaData();
- for (int i = 0; i < metaDataList.size(); ++i) {
- const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap();
- const QCborArray keys = metaData.value("Keys"_L1).toArray();
+ const QList<QCborArray> metaDataList = metaDataKeys();
+ for (int i = 0; i < int(metaDataList.size()); ++i) {
+ const QCborArray &keys = metaDataList[i];
for (QCborValueConstRef key : keys)
result.insert(i, key.toString());
}
@@ -409,10 +596,9 @@ QMultiMap<int, QString> QFactoryLoader::keyMap() const
int QFactoryLoader::indexOf(const QString &needle) const
{
- const QList<QPluginParsedMetaData> metaDataList = metaData();
- for (int i = 0; i < metaDataList.size(); ++i) {
- const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap();
- const QCborArray keys = metaData.value("Keys"_L1).toArray();
+ const QList<QCborArray> metaDataList = metaDataKeys();
+ for (int i = 0; i < int(metaDataList.size()); ++i) {
+ const QCborArray &keys = metaDataList[i];
for (QCborValueConstRef key : keys) {
if (key.toString().compare(needle, Qt::CaseInsensitive) == 0)
return i;
diff --git a/src/corelib/plugin/qfactoryloader_p.h b/src/corelib/plugin/qfactoryloader_p.h
index 0c8f977707..56dc7e6ad1 100644
--- a/src/corelib/plugin/qfactoryloader_p.h
+++ b/src/corelib/plugin/qfactoryloader_p.h
@@ -74,9 +74,9 @@ public:
void update();
static void refreshAll();
-#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC)
+#if defined(Q_OS_UNIX) && !defined (Q_OS_DARWIN)
QLibraryPrivate *library(const QString &key) const;
-#endif // Q_OS_UNIX && !Q_OS_MAC
+#endif // Q_OS_UNIX && !Q_OS_DARWIN
#endif // QT_CONFIG(library)
void setExtraSearchPath(const QString &path);
@@ -86,6 +86,7 @@ public:
using MetaDataList = QList<QPluginParsedMetaData>;
MetaDataList metaData() const;
+ QList<QCborArray> metaDataKeys() const;
QObject *instance(int index) const;
};
diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp
index abfb1e0df5..a3ef8e3c52 100644
--- a/src/corelib/plugin/qlibrary.cpp
+++ b/src/corelib/plugin/qlibrary.cpp
@@ -11,12 +11,11 @@
#include <qfile.h>
#include <qfileinfo.h>
#include <qjsondocument.h>
-#include <qmap.h>
#include <qmutex.h>
#include <qoperatingsystemversion.h>
#include <qstringlist.h>
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
# include <private/qcore_mac_p.h>
#endif
#include <private/qcoreapplication_p.h>
@@ -30,10 +29,15 @@
#include <qtcore_tracepoints_p.h>
+#include <QtCore/q20map.h>
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+Q_TRACE_POINT(qtcore, QLibraryPrivate_load_entry, const QString &fileName);
+Q_TRACE_POINT(qtcore, QLibraryPrivate_load_exit, bool success);
+
// On Unix systema and on Windows with MinGW, we can mix and match debug and
// release plugins without problems. (unless compiled in debug-and-release mode
// - why?)
@@ -207,7 +211,7 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin
information could not be read.
Returns true if version information is present and successfully read.
*/
-static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
+static QLibraryScanResult findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
{
QFile file(library);
if (!file.open(QIODevice::ReadOnly)) {
@@ -215,7 +219,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
lib->errorString = file.errorString();
qCWarning(qt_lcDebugPlugins, "%ls: cannot open: %ls", qUtf16Printable(library),
qUtf16Printable(file.errorString()));
- return false;
+ return {};
}
// Files can be bigger than the virtual memory size on 32-bit systems, so
@@ -232,7 +236,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
// This can't be used as a plugin.
qCWarning(qt_lcDebugPlugins, "%ls: failed to map to memory: %ls",
qUtf16Printable(library), qUtf16Printable(file.errorString()));
- return false;
+ return {};
}
#else
QByteArray data;
@@ -249,15 +253,19 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
QString errMsg = library;
QLibraryScanResult r = qt_find_pattern(filedata, fdlen, &errMsg);
if (r.length) {
+#if defined(Q_OF_MACH_O)
+ if (r.isEncrypted)
+ return r;
+#endif
if (!lib->metaData.parse(QByteArrayView(filedata + r.pos, r.length))) {
errMsg = lib->metaData.errorString();
- qCWarning(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls",
+ qCDebug(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls",
qUtf16Printable(library), qUtf16Printable(errMsg));
} else {
qCDebug(qt_lcDebugPlugins, "Found metadata in lib %ls, metadata=\n%s\n",
qUtf16Printable(library),
QJsonDocument(lib->metaData.toJson()).toJson().constData());
- return true;
+ return r;
}
} else {
qCDebug(qt_lcDebugPlugins, "Failed to find metadata in lib %ls: %ls",
@@ -266,7 +274,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1': %2")
.arg(library, errMsg);
- return false;
+ return {};
}
static void installCoverageTool(QLibraryPrivate *libPrivate)
@@ -314,7 +322,7 @@ private:
static inline QLibraryStore *instance();
// all members and instance() are protected by qt_library_mutex
- typedef QMap<QString, QLibraryPrivate *> LibraryMap;
+ typedef std::map<QString, QLibraryPrivate *> LibraryMap;
LibraryMap libraryMap;
};
@@ -334,9 +342,7 @@ inline void QLibraryStore::cleanup()
return;
// find any libraries that are still loaded but have a no one attached to them
- LibraryMap::Iterator it = data->libraryMap.begin();
- for (; it != data->libraryMap.end(); ++it) {
- QLibraryPrivate *lib = it.value();
+ for (auto &[_, lib] : data->libraryMap) {
if (lib->libraryRefCount.loadRelaxed() == 1) {
if (lib->libraryUnloadCount.loadRelaxed() > 0) {
Q_ASSERT(lib->pHnd.loadRelaxed());
@@ -350,14 +356,13 @@ inline void QLibraryStore::cleanup()
lib->unload();
#endif
}
- delete lib;
- it.value() = nullptr;
+ delete std::exchange(lib, nullptr);
}
}
// dump all objects that remain
if (lcDebugLibrary().isDebugEnabled()) {
- for (QLibraryPrivate *lib : std::as_const(data->libraryMap)) {
+ for (auto &[_, lib] : data->libraryMap) {
if (lib)
qDebug(lcDebugLibrary)
<< "On QtCore unload," << lib->fileName << "was leaked, with"
@@ -388,26 +393,34 @@ QLibraryStore *QLibraryStore::instance()
inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version,
QLibrary::LoadHints loadHints)
{
+ auto lazyNewLib = [&] {
+ auto result = new QLibraryPrivate(fileName, version, loadHints);
+ result->libraryRefCount.ref();
+ return result;
+ };
+
+ if (fileName.isEmpty()) // request for empty d-pointer in QLibrary::setLoadHints();
+ return lazyNewLib(); // must return an independent (new) object
+
QMutexLocker locker(&qt_library_mutex);
QLibraryStore *data = instance();
+ if (Q_UNLIKELY(!data)) {
+ locker.unlock();
+ return lazyNewLib();
+ }
+
QString mapName = version.isEmpty() ? fileName : fileName + u'\0' + version;
- // check if this library is already loaded
- QLibraryPrivate *lib = nullptr;
- if (Q_LIKELY(data)) {
- lib = data->libraryMap.value(mapName);
- if (lib)
- lib->mergeLoadHints(loadHints);
+ QLibraryPrivate *&lib = data->libraryMap[std::move(mapName)];
+ if (lib) {
+ // already loaded
+ lib->libraryRefCount.ref();
+ lib->mergeLoadHints(loadHints);
+ } else {
+ lib = lazyNewLib();
}
- if (!lib)
- lib = new QLibraryPrivate(fileName, version, loadHints);
- // track this library
- if (Q_LIKELY(data) && !fileName.isEmpty())
- data->libraryMap.insert(mapName, lib);
-
- lib->libraryRefCount.ref();
return lib;
}
@@ -425,8 +438,9 @@ inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib)
Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0);
if (Q_LIKELY(data) && !lib->fileName.isEmpty()) {
- qsizetype n = erase_if(data->libraryMap, [lib](LibraryMap::iterator it) {
- return it.value() == lib;
+ using q20::erase_if;
+ const auto n = erase_if(data->libraryMap, [lib](const auto &e) {
+ return e.second == lib;
});
Q_ASSERT_X(n, "~QLibrary", "Did not find this library in the library map");
Q_UNUSED(n);
@@ -458,7 +472,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh)
if (pHnd.loadRelaxed())
return;
- loadHintsInt.storeRelaxed(lh.toInt());
+ loadHintsInt.fetchAndOrRelaxed(lh.toInt());
}
QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
@@ -470,6 +484,13 @@ QFunctionPointer QLibraryPrivate::resolve(const char *symbol)
void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh)
{
+ // Set the load hints directly for a dummy if this object is not associated
+ // with a file. Such object is not shared between multiple instances.
+ if (fileName.isEmpty()) {
+ loadHintsInt.storeRelaxed(lh.toInt());
+ return;
+ }
+
// this locks a global mutex
QMutexLocker lock(&qt_library_mutex);
mergeLoadHints(lh);
@@ -705,7 +726,7 @@ void QLibraryPrivate::updatePluginState()
bool success = false;
-#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
+#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
if (fileName.endsWith(".debug"_L1)) {
// refuse to load a file that ends in .debug
// these are the debug symbols from the libraries
@@ -721,7 +742,22 @@ void QLibraryPrivate::updatePluginState()
if (!pHnd.loadRelaxed()) {
// scan for the plugin metadata without loading
- success = findPatternUnloaded(fileName, this);
+ QLibraryScanResult result = findPatternUnloaded(fileName, this);
+#if defined(Q_OF_MACH_O)
+ if (result.length && result.isEncrypted) {
+ // We found the .qtmetadata section, but since the library is encrypted
+ // we need to dlopen() it before we can parse the metadata for further
+ // validation.
+ qCDebug(qt_lcDebugPlugins, "Library is encrypted. Doing prospective load before parsing metadata");
+ locker.unlock();
+ load();
+ locker.relock();
+ success = qt_get_metadata(this, &errorString);
+ } else
+#endif
+ {
+ success = result.length != 0;
+ }
} else {
// library is already loaded (probably via QLibrary)
// simply get the target function and call it.
@@ -744,7 +780,7 @@ void QLibraryPrivate::updatePluginState()
uint qt_version = uint(metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger());
bool debug = metaData.value(QtPluginMetaDataKeys::IsDebug).toBool();
if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) {
- qCWarning(qt_lcDebugPlugins, "In %s:\n"
+ qCDebug(qt_lcDebugPlugins, "In %s:\n"
" Plugin uses incompatible Qt library (%d.%d.%d) [%s]",
QFile::encodeName(fileName).constData(),
(qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff,
@@ -798,7 +834,9 @@ bool QLibrary::load()
call will fail, and unloading will only happen when every instance
has called unload().
- Note that on Mac OS X 10.3 (Panther), dynamic libraries cannot be unloaded.
+ Note that on \macos, dynamic libraries cannot be unloaded.
+ QLibrary::unload() will return \c true, but the library will remain
+ loaded into the process.
\sa resolve(), load()
*/
@@ -812,13 +850,17 @@ bool QLibrary::unload()
}
/*!
- Returns \c true if the library is loaded; otherwise returns \c false.
+ Returns \c true if load() succeeded; otherwise returns \c false.
+
+ \note Prior to Qt 6.6, this function would return \c true even without a
+ call to load() if another QLibrary object on the same library had caused it
+ to be loaded.
\sa load()
*/
bool QLibrary::isLoaded() const
{
- return d && d->pHnd.loadRelaxed();
+ return d.tag() == Loaded;
}
@@ -955,8 +997,7 @@ void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &ver
d->release();
}
QLibraryPrivate *dd = QLibraryPrivate::findOrCreate(fileName, version, lh);
- d = dd;
- d.setTag(isLoaded() ? Loaded : NotLoaded);
+ d = QTaggedPointer(dd, NotLoaded); // we haven't load()ed
}
/*!
@@ -1091,6 +1132,10 @@ QString QLibrary::errorString() const
lazy symbol resolution, and will not export external symbols for resolution
in other dynamically-loaded libraries.
+ \note Hints can only be cleared when this object is not associated with a
+ file. Hints can only be added once the file name is set (\a hints will
+ be or'ed with the old hints).
+
\note Setting this property after the library has been loaded has no effect
and loadHints() will not reflect those changes.
diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h
index 8c722a3283..87d36ee5c8 100644
--- a/src/corelib/plugin/qlibrary_p.h
+++ b/src/corelib/plugin/qlibrary_p.h
@@ -28,6 +28,8 @@
# include "QtCore/qt_windows.h"
#endif
+#include <memory>
+
QT_REQUIRE_CONFIG(library);
QT_BEGIN_NAMESPACE
@@ -38,6 +40,9 @@ struct QLibraryScanResult
{
qsizetype pos;
qsizetype length;
+#if defined(Q_OF_MACH_O)
+ bool isEncrypted = false;
+#endif
};
class QLibraryStore;
@@ -51,6 +56,12 @@ public:
#endif
enum UnloadFlag { UnloadSys, NoUnloadSys };
+ struct Deleter {
+ // QLibraryPrivate::release() is not, yet, and cannot easily be made, noexcept:
+ void operator()(QLibraryPrivate *p) const { p->release(); }
+ };
+ using UniquePtr = std::unique_ptr<QLibraryPrivate, Deleter>;
+
const QString fileName;
const QString fullVersion;
diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp
index 898d5a2f6c..a6fb5403cd 100644
--- a/src/corelib/plugin/qlibrary_unix.cpp
+++ b/src/corelib/plugin/qlibrary_unix.cpp
@@ -12,7 +12,7 @@
#include <dlfcn.h>
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
# include <private/qcore_mac_p.h>
#endif
@@ -25,12 +25,6 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-static QString qdlerror()
-{
- const char *err = dlerror();
- return err ? u'(' + QString::fromLocal8Bit(err) + u')' : QString();
-}
-
QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion)
{
QStringList suffixes;
@@ -72,7 +66,7 @@ QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion)
# endif
}
#endif
-# ifdef Q_OS_MAC
+# ifdef Q_OS_DARWIN
if (!fullVersion.isEmpty()) {
suffixes << ".%1.bundle"_L1.arg(fullVersion);
suffixes << ".%1.dylib"_L1.arg(fullVersion);
@@ -90,6 +84,11 @@ QStringList QLibraryPrivate::prefixes_sys()
bool QLibraryPrivate::load_sys()
{
+#if defined(Q_OS_WASM) && defined(QT_STATIC)
+ // emscripten does not support dlopen when using static linking
+ return false;
+#endif
+
QMutexLocker locker(&mutex);
QString attempt;
QFileSystemEntry fsEntry(fileName);
@@ -194,7 +193,7 @@ bool QLibraryPrivate::load_sys()
continue;
if (loadHints & QLibrary::LoadArchiveMemberHint) {
attempt = name;
- int lparen = attempt.indexOf(u'(');
+ qsizetype lparen = attempt.indexOf(u'(');
if (lparen == -1)
lparen = attempt.size();
attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
@@ -208,14 +207,6 @@ bool QLibraryPrivate::load_sys()
auto attemptFromBundle = attempt;
hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(u'/', u'_')), dlFlags);
}
- if (hnd) {
- using JniOnLoadPtr = jint (*)(JavaVM *vm, void *reserved);
- JniOnLoadPtr jniOnLoad = reinterpret_cast<JniOnLoadPtr>(dlsym(hnd, "JNI_OnLoad"));
- if (jniOnLoad && jniOnLoad(QJniEnvironment::javaVM(), nullptr) == JNI_ERR) {
- dlclose(hnd);
- hnd = nullptr;
- }
- }
#endif
if (!hnd && fileName.startsWith(u'/') && QFile::exists(attempt)) {
@@ -228,7 +219,7 @@ bool QLibraryPrivate::load_sys()
}
}
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
if (!hnd) {
QByteArray utf8Bundle = fileName.toUtf8();
QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true);
@@ -245,7 +236,8 @@ bool QLibraryPrivate::load_sys()
locker.relock();
if (!hnd) {
- errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror());
+ errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName,
+ QLatin1StringView(dlerror()));
}
if (hnd) {
qualifiedFileName = attempt;
@@ -257,36 +249,27 @@ bool QLibraryPrivate::load_sys()
bool QLibraryPrivate::unload_sys()
{
- if (dlclose(pHnd.loadAcquire())) {
-#if defined (Q_OS_QNX) // Workaround until fixed in QNX; fixes crash in
- char *error = dlerror(); // QtDeclarative auto test "qqmlenginecleanup" for instance
+ bool doTryUnload = true;
+#ifndef RTLD_NODELETE
+ if (loadHints() & QLibrary::PreventUnloadHint)
+ doTryUnload = false;
+#endif
+ if (doTryUnload && dlclose(pHnd.loadAcquire())) {
+ const char *error = dlerror();
+#if defined (Q_OS_QNX)
+ // Workaround until fixed in QNX; fixes crash in
+ // QtDeclarative auto test "qqmlenginecleanup" for instance
if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative"
return true;
+#endif
errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName,
QLatin1StringView(error));
-#else
- errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName, qdlerror());
-#endif
return false;
}
errorString.clear();
return true;
}
-#if defined(Q_OS_LINUX)
-Q_CORE_EXPORT QFunctionPointer qt_linux_find_symbol_sys(const char *symbol)
-{
- return QFunctionPointer(dlsym(RTLD_DEFAULT, symbol));
-}
-#endif
-
-#ifdef Q_OS_MAC
-Q_CORE_EXPORT QFunctionPointer qt_mac_resolve_sys(void *handle, const char *symbol)
-{
- return QFunctionPointer(dlsym(handle, symbol));
-}
-#endif
-
QFunctionPointer QLibraryPrivate::resolve_sys(const char *symbol)
{
QFunctionPointer address = QFunctionPointer(dlsym(pHnd.loadAcquire(), symbol));
diff --git a/src/corelib/plugin/qmachparser.cpp b/src/corelib/plugin/qmachparser.cpp
index 979ce2c7de..7a82b84cb3 100644
--- a/src/corelib/plugin/qmachparser.cpp
+++ b/src/corelib/plugin/qmachparser.cpp
@@ -56,6 +56,23 @@ static QLibraryScanResult notfound(const QString &reason, QString *errorString)
return {};
}
+static bool isEncrypted(const my_mach_header *header)
+{
+ auto commandCursor = uintptr_t(header) + sizeof(my_mach_header);
+ for (uint32_t i = 0; i < header->ncmds; ++i) {
+ load_command *loadCommand = reinterpret_cast<load_command *>(commandCursor);
+ if (loadCommand->cmd == LC_ENCRYPTION_INFO || loadCommand->cmd == LC_ENCRYPTION_INFO_64) {
+ // The layout of encryption_info_command and encryption_info_command_64 is the same
+ // up until and including cryptid, so we can treat it as encryption_info_command.
+ auto encryptionInfoCommand = reinterpret_cast<encryption_info_command*>(loadCommand);
+ return encryptionInfoCommand->cryptid != 0;
+ }
+ commandCursor += loadCommand->cmdsize;
+ }
+
+ return false;
+}
+
QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *errorString)
{
// The minimum size of a Mach-O binary we're interested in.
@@ -166,8 +183,12 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e
if (sect[j].size < sizeof(QPluginMetaData::MagicHeader))
return notfound(QLibrary::tr(".qtmetadata section is too small"), errorString);
+ const bool binaryIsEncrypted = isEncrypted(header);
qsizetype pos = reinterpret_cast<const char *>(header) - m_s + sect[j].offset;
- if (IncludeValidityChecks) {
+
+ // We can not read the section data of encrypted libraries until they
+ // have been dlopened(), so skip validity check if that's the case.
+ if (IncludeValidityChecks && !binaryIsEncrypted) {
QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString);
QByteArrayView actualMagic = QByteArrayView(m_s + pos, expectedMagic.size());
if (expectedMagic != actualMagic)
@@ -175,7 +196,7 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e
}
pos += sizeof(QPluginMetaData::MagicString);
- return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)) };
+ return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)), binaryIsEncrypted };
}
}
diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h
index 30f5cbb92f..909c8acdcc 100644
--- a/src/corelib/plugin/qplugin.h
+++ b/src/corelib/plugin/qplugin.h
@@ -127,7 +127,7 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin);
#if defined(Q_OF_ELF) || (defined(Q_OS_WIN) && (defined (Q_CC_GNU) || defined(Q_CC_CLANG)))
# define QT_PLUGIN_METADATA_SECTION \
__attribute__ ((section (".qtmetadata"))) __attribute__((used))
-#elif defined(Q_OS_MAC)
+#elif defined(Q_OS_DARWIN)
# define QT_PLUGIN_METADATA_SECTION \
__attribute__ ((section ("__TEXT,qtmetadata"))) __attribute__((used))
#elif defined(Q_CC_MSVC)
diff --git a/src/corelib/plugin/qplugin.qdoc b/src/corelib/plugin/qplugin.qdoc
index dd4aa5f4b0..0ca248a548 100644
--- a/src/corelib/plugin/qplugin.qdoc
+++ b/src/corelib/plugin/qplugin.qdoc
@@ -19,13 +19,10 @@
This macro associates the given \a Identifier (a string literal)
to the interface class called \a ClassName. The \a Identifier must
- be unique. For example:
-
- \snippet plugandpaint/app/interfaces.h 3
+ be unique.
This macro is normally used right after the class definition for
- \a ClassName, in a header file. See the
- \l{tools/plugandpaint/app}{Plug & Paint} example for details.
+ \a ClassName, in a header file.
If you want to use Q_DECLARE_INTERFACE with interface classes
declared in a namespace then you have to make sure the Q_DECLARE_INTERFACE
@@ -53,8 +50,6 @@
\snippet code/doc_src_qplugin.cpp 1
- See the \l{tools/plugandpaint/app}{Plug & Paint} example for details.
-
Note that the class this macro appears on must be default-constructible.
FILE is optional and points to a json file.
diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp
index 599256783e..03b8cfbb84 100644
--- a/src/corelib/plugin/qpluginloader.cpp
+++ b/src/corelib/plugin/qpluginloader.cpp
@@ -71,7 +71,7 @@ using namespace Qt::StringLiterals;
link to plugins statically. You can use QLibrary if you need to
load dynamic libraries in a statically linked application.
- \sa QLibrary, {Plug & Paint Example}
+ \sa QLibrary
*/
static constexpr QLibrary::LoadHints defaultLoadHints = QLibrary::PreventUnloadHint;
@@ -235,7 +235,7 @@ static QString locatePlugin(const QString& fileName)
suffixes.prepend(QString());
// Split up "subdir/filename"
- const int slash = fileName.lastIndexOf(u'/');
+ const qsizetype slash = fileName.lastIndexOf(u'/');
const auto baseName = QStringView{fileName}.mid(slash + 1);
const auto basePath = isAbsolute ? QStringView() : QStringView{fileName}.left(slash + 1); // keep the '/'
@@ -349,10 +349,11 @@ QString QPluginLoader::errorString() const
void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints)
{
if (!d) {
- d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr
+ d = QLibraryPrivate::findOrCreate({}, {}, loadHints); // ugly, but we need a d-ptr
d->errorString.clear();
+ } else {
+ d->setLoadHints(loadHints);
}
- d->setLoadHints(loadHints);
}
QLibrary::LoadHints QPluginLoader::loadHints() const
@@ -379,7 +380,19 @@ Q_GLOBAL_STATIC(StaticPluginList, staticPluginList)
*/
void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
{
- staticPluginList()->append(plugin);
+ // using operator* because we shouldn't be registering plugins while
+ // unloading the application!
+ StaticPluginList &plugins = *staticPluginList;
+
+ // insert the plugin in the list, sorted by address, so we can detect
+ // duplicate registrations
+ auto comparator = [=](const QStaticPlugin &p1, const QStaticPlugin &p2) {
+ using Less = std::less<decltype(plugin.instance)>;
+ return Less{}(p1.instance, p2.instance);
+ };
+ auto pos = std::lower_bound(plugins.constBegin(), plugins.constEnd(), plugin, comparator);
+ if (pos == plugins.constEnd() || pos->instance != plugin.instance)
+ plugins.insert(pos, plugin);
}
/*!
@@ -390,12 +403,11 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin)
QObjectList QPluginLoader::staticInstances()
{
QObjectList instances;
- const StaticPluginList *plugins = staticPluginList();
- if (plugins) {
- const int numPlugins = plugins->size();
- instances.reserve(numPlugins);
- for (int i = 0; i < numPlugins; ++i)
- instances += plugins->at(i).instance();
+ if (staticPluginList.exists()) {
+ const StaticPluginList &plugins = *staticPluginList;
+ instances.reserve(plugins.size());
+ for (QStaticPlugin plugin : plugins)
+ instances += plugin.instance();
}
return instances;
}
diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp
index 8613f96c21..8c3b758aac 100644
--- a/src/corelib/plugin/quuid.cpp
+++ b/src/corelib/plugin/quuid.cpp
@@ -13,6 +13,9 @@
QT_BEGIN_NAMESPACE
+// ensure QList of this is efficient
+static_assert(QTypeInfo<QUuid::Id128Bytes>::isRelocatable);
+
// 16 bytes (a uint, two shorts and a uchar[8]), each represented by two hex
// digits; plus four dashes and a pair of enclosing brace: 16*2 + 4 + 2 = 38.
enum { MaxStringUuidLength = 38 };
@@ -116,7 +119,7 @@ static QUuid _q_uuidFromHex(const char *src)
static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCryptographicHash::Algorithm algorithm, int version)
{
QCryptographicHash hash(algorithm);
- hash.addData(ns.toRfc4122());
+ hash.addData(QByteArrayView{ns.toBytes()});
hash.addData(baseData);
QByteArrayView hashResult = hash.resultView();
Q_ASSERT(hashResult.size() >= 16);
@@ -139,6 +142,11 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto
\reentrant
+ \compares strong
+ \compareswith strong GUID
+ \note Comparison with GUID is Windows-only.
+ \endcompareswith
+
Using \e{U}niversally \e{U}nique \e{ID}entifiers (UUID) is a
standard way to uniquely identify entities in a distributed
computing environment. A UUID is a 16-byte (128-bit) number
@@ -287,6 +295,125 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto
*/
/*!
+ \class QUuid::Id128Bytes
+ \inmodule QtCore
+ \since 6.6
+
+ This trivial structure is 128 bits (16 bytes) in size and holds the binary
+ representation of a UUID. Applications can \c{memcpy()} its contents to and
+ from many other libraries' UUID or GUID structures that take 128-bit
+ values.
+*/
+
+/*!
+ \fn QUuid::Id128Bytes qFromBigEndian(QUuid::Id128Bytes src)
+ \since 6.6
+ \relates QUuid::Id128Bytes
+ \overload
+
+ Converts \a src from big-endian byte order and returns the struct holding
+ the binary representation of UUID in host byte order.
+
+ \sa <QtEndian>
+*/
+
+/*!
+ \fn QUuid::Id128Bytes qFromLittleEndian(QUuid::Id128Bytes src)
+ \since 6.6
+ \relates QUuid::Id128Bytes
+ \overload
+
+ Converts \a src from little-endian byte order and returns the struct holding
+ the binary representation of UUID in host byte order.
+
+ \sa <QtEndian>
+*/
+
+/*!
+ \fn QUuid::Id128Bytes qToBigEndian(QUuid::Id128Bytes src)
+ \since 6.6
+ \relates QUuid::Id128Bytes
+ \overload
+
+ Converts \a src from host byte order and returns the struct holding the
+ binary representation of UUID in big-endian byte order.
+
+ \sa <QtEndian>
+*/
+
+/*!
+ \fn QUuid::Id128Bytes qToLittleEndian(QUuid::Id128Bytes src)
+ \since 6.6
+ \relates QUuid::Id128Bytes
+ \overload
+
+ Converts \a src from host byte order and returns the struct holding the
+ binary representation of UUID in little-endian byte order.
+
+ \sa <QtEndian>
+*/
+
+/*!
+ \fn QUuid::QUuid(Id128Bytes id128, QSysInfo::Endian order) noexcept
+ \since 6.6
+
+ Creates a QUuid based on the integral \a id128 parameter. The input
+ \a id128 parameter is considered to have byte order \a order.
+
+ \sa fromBytes(), toBytes(), toRfc4122(), toUInt128()
+*/
+
+/*!
+ \fn QUuid::fromUInt128(quint128 uuid, QSysInfo::Endian order) noexcept
+ \since 6.6
+
+ Creates a QUuid based on the integral \a uuid parameter. The input \a uuid
+ parameter is considered to have byte order \a order.
+
+ \note This function is only present on platforms that offer a 128-bit
+ integer type.
+
+ \sa toUInt128(), fromBytes(), toBytes(), toRfc4122()
+*/
+
+/*!
+ \fn quint128 QUuid::toUInt128(QSysInfo::Endian order) const noexcept
+ \since 6.6
+
+ Returns a 128-bit integer created from this QUuid on the byte order
+ specified by \a order. The binary content of this function is the same as
+ toRfc4122() if the order is QSysInfo::BigEndian. See that function for more
+ details.
+
+ \note This function is only present on platforms that offer a 128-bit
+ integer type.
+
+ \sa toRfc4122(), fromUInt128(), toBytes(), fromBytes(), QUuid()
+*/
+
+/*!
+ \fn QUuid::Id128Bytes QUuid::toBytes(QSysInfo::Endian order) const noexcept
+ \since 6.6
+
+ Returns a 128-bit ID created from this QUuid on the byte order specified
+ by \a order. The binary content of this function is the same as toRfc4122()
+ if the order is QSysInfo::BigEndian. See that function for more details.
+
+ \sa toRfc4122(), fromBytes(), QUuid()
+*/
+
+/*!
+ \fn QUuid QUuid::fromBytes(const void *bytes, QSysInfo::Endian order) noexcept
+ \since 6.6
+
+ Reads 128 bits (16 bytes) from \a bytes using byte order \a order and
+ returns the QUuid corresponding to those bytes. This function does the same
+ as fromRfc4122() if the byte order \a order is QSysInfo::BigEndian.
+
+ \sa fromRfc4122()
+*/
+
+/*!
\fn QUuid::QUuid(const GUID &guid)
Casts a Windows \a guid to a Qt QUuid.
@@ -468,45 +595,26 @@ QUuid QUuid::createUuidV5(const QUuid &ns, const QByteArray &baseData)
\since 4.8
- \sa toRfc4122(), QUuid()
+ \sa toRfc4122(), QUuid(), fromBytes()
*/
QUuid QUuid::fromRfc4122(QByteArrayView bytes) noexcept
{
if (bytes.isEmpty() || bytes.size() != 16)
return QUuid();
-
- uint d1;
- ushort d2, d3;
- uchar d4[8];
-
- const uchar *data = reinterpret_cast<const uchar *>(bytes.data());
-
- d1 = qFromBigEndian<quint32>(data);
- data += sizeof(quint32);
- d2 = qFromBigEndian<quint16>(data);
- data += sizeof(quint16);
- d3 = qFromBigEndian<quint16>(data);
- data += sizeof(quint16);
-
- for (int i = 0; i < 8; ++i) {
- d4[i] = *(data);
- data++;
- }
-
- return QUuid(d1, d2, d3, d4[0], d4[1], d4[2], d4[3], d4[4], d4[5], d4[6], d4[7]);
+ return fromBytes(bytes.data());
}
/*!
- \fn bool QUuid::operator==(const QUuid &other) const
+ \fn bool QUuid::operator==(const QUuid &lhs, const QUuid &rhs)
- Returns \c true if this QUuid and the \a other QUuid are identical;
+ Returns \c true if \a lhs QUuid and the \a rhs QUuid are identical;
otherwise returns \c false.
*/
/*!
- \fn bool QUuid::operator!=(const QUuid &other) const
+ \fn bool QUuid::operator!=(const QUuid &lhs, const QUuid &rhs)
- Returns \c true if this QUuid and the \a other QUuid are different;
+ Returns \c true if \a lhs QUuid and the \a rhs QUuid are different;
otherwise returns \c false.
*/
@@ -623,27 +731,16 @@ QByteArray QUuid::toByteArray(QUuid::StringFormat mode) const
\endtable
+ The bytes in the byte array returned by this function contains the same
+ binary content as toBytes().
+
+ \sa toBytes()
\since 4.8
*/
QByteArray QUuid::toRfc4122() const
{
- // we know how many bytes a UUID has, I hope :)
- QByteArray bytes(16, Qt::Uninitialized);
- uchar *data = reinterpret_cast<uchar *>(bytes.data());
-
- qToBigEndian(data1, data);
- data += sizeof(quint32);
- qToBigEndian(data2, data);
- data += sizeof(quint16);
- qToBigEndian(data3, data);
- data += sizeof(quint16);
-
- for (int i = 0; i < 8; ++i) {
- *(data) = data4[i];
- data++;
- }
-
- return bytes;
+ Id128Bytes bytes = toBytes();
+ return QByteArrayView(bytes).toByteArray();
}
#ifndef QT_NO_DATASTREAM
@@ -653,14 +750,19 @@ QByteArray QUuid::toRfc4122() const
*/
QDataStream &operator<<(QDataStream &s, const QUuid &id)
{
- QByteArray bytes;
+ constexpr int NumBytes = sizeof(QUuid);
+ static_assert(NumBytes == 16, "Change the serialization format when this ever hits");
+ char bytes[NumBytes];
if (s.byteOrder() == QDataStream::BigEndian) {
- bytes = id.toRfc4122();
+ const auto id128 = id.toBytes();
+ static_assert(sizeof(id128) == NumBytes);
+ memcpy(bytes, &id128, NumBytes);
} else {
- // we know how many bytes a UUID has, I hope :)
- bytes = QByteArray(16, Qt::Uninitialized);
- uchar *data = reinterpret_cast<uchar *>(bytes.data());
+ auto *data = bytes;
+ // for historical reasons, our little-endian serialization format
+ // stores each of the UUID fields in little endian, instead of storing
+ // a little endian Id128
qToLittleEndian(id.data1, data);
data += sizeof(quint32);
qToLittleEndian(id.data2, data);
@@ -674,9 +776,9 @@ QDataStream &operator<<(QDataStream &s, const QUuid &id)
}
}
- if (s.writeRawData(bytes.data(), 16) != 16) {
+ if (s.writeRawData(bytes, NumBytes) != NumBytes)
s.setStatus(QDataStream::WriteFailed);
- }
+
return s;
}
@@ -801,51 +903,31 @@ QUuid::Version QUuid::version() const noexcept
}
/*!
- \fn bool QUuid::operator<(const QUuid &other) const
+ \fn bool QUuid::operator<(const QUuid &lhs, const QUuid &rhs)
- Returns \c true if this QUuid has the same \l{Variant field}
- {variant field} as the \a other QUuid and is lexicographically
- \e{before} the \a other QUuid. If the \a other QUuid has a
+ Returns \c true if \a lhs QUuid has the same \l{Variant field}
+ {variant field} as the \a rhs QUuid and is lexicographically
+ \e{before} the \a rhs QUuid. If the \a rhs QUuid has a
different variant field, the return value is determined by
comparing the two \l{QUuid::Variant} {variants}.
\sa variant()
*/
-bool QUuid::operator<(const QUuid &other) const noexcept
-{
- if (variant() != other.variant())
- return variant() < other.variant();
-
-#define ISLESS(f1, f2) if (f1!=f2) return (f1<f2);
- ISLESS(data1, other.data1);
- ISLESS(data2, other.data2);
- ISLESS(data3, other.data3);
- for (int n = 0; n < 8; n++) {
- ISLESS(data4[n], other.data4[n]);
- }
-#undef ISLESS
- return false;
-}
/*!
- \fn bool QUuid::operator>(const QUuid &other) const
+ \fn bool QUuid::operator>(const QUuid &lhs, const QUuid &rhs)
- Returns \c true if this QUuid has the same \l{Variant field}
- {variant field} as the \a other QUuid and is lexicographically
- \e{after} the \a other QUuid. If the \a other QUuid has a
+ Returns \c true if \a lhs QUuid has the same \l{Variant field}
+ {variant field} as the \a rhs QUuid and is lexicographically
+ \e{after} the \a rhs QUuid. If the \a rhs QUuid has a
different variant field, the return value is determined by
comparing the two \l{QUuid::Variant} {variants}.
\sa variant()
*/
-bool QUuid::operator>(const QUuid &other) const noexcept
-{
- return other < *this;
-}
/*!
- \fn bool operator<=(const QUuid &lhs, const QUuid &rhs)
- \relates QUuid
+ \fn bool QUuid::operator<=(const QUuid &lhs, const QUuid &rhs)
\since 5.5
Returns \c true if \a lhs has the same \l{Variant field}
@@ -858,8 +940,7 @@ bool QUuid::operator>(const QUuid &other) const noexcept
*/
/*!
- \fn bool operator>=(const QUuid &lhs, const QUuid &rhs)
- \relates QUuid
+ \fn bool QUuid::operator>=(const QUuid &lhs, const QUuid &rhs)
\since 5.5
Returns \c true if \a lhs has the same \l{Variant field}
@@ -895,7 +976,7 @@ QUuid QUuid::createUuid()
return result;
}
-#else // Q_OS_WIN
+#elif !defined(QT_BOOTSTRAPPED)
QUuid QUuid::createUuid()
{
@@ -909,20 +990,20 @@ QUuid QUuid::createUuid()
return result;
}
-#endif // !Q_OS_WIN
+#endif // !Q_OS_WIN && !QT_BOOTSTRAPPED
/*!
- \fn bool QUuid::operator==(const GUID &guid) const
+ \fn bool QUuid::operator==(const QUuid &lhs, const GUID &rhs)
- Returns \c true if this UUID is equal to the Windows GUID \a guid;
+ Returns \c true if \a lhs UUID is equal to the Windows GUID \a rhs;
otherwise returns \c false.
*/
/*!
- \fn bool QUuid::operator!=(const GUID &guid) const
+ \fn bool QUuid::operator!=(const QUuid &lhs, const GUID &rhs)
- Returns \c true if this UUID is not equal to the Windows GUID \a
- guid; otherwise returns \c false.
+ Returns \c true if \a lhs UUID is not equal to the Windows GUID \a rhs;
+ otherwise returns \c false.
*/
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/corelib/plugin/quuid.h b/src/corelib/plugin/quuid.h
index 64aa891ba1..14029f1abc 100644
--- a/src/corelib/plugin/quuid.h
+++ b/src/corelib/plugin/quuid.h
@@ -4,6 +4,8 @@
#ifndef QUUID_H
#define QUUID_H
+#include <QtCore/qcompare.h>
+#include <QtCore/qendian.h>
#include <QtCore/qstring.h>
#if defined(Q_OS_WIN) || defined(Q_QDOC)
@@ -26,7 +28,6 @@ Q_FORWARD_DECLARE_OBJC_CLASS(NSUUID);
QT_BEGIN_NAMESPACE
-
class Q_CORE_EXPORT QUuid
{
QUuid(Qt::Initialization) {}
@@ -55,11 +56,44 @@ public:
Id128 = 3
};
+ union alignas(16) Id128Bytes {
+ quint8 data[16];
+ quint16 data16[8];
+ quint32 data32[4];
+ quint64 data64[2];
+#if defined(__SIZEOF_INT128__)
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wpedantic") // ISO C++ does not support ‘__int128’ for ‘data128’
+ unsigned __int128 data128[1];
+QT_WARNING_POP
+#elif defined(QT_SUPPORTS_INT128)
+# error "struct QUuid::Id128Bytes should not depend on QT_SUPPORTS_INT128 for ABI reasons."
+# error "Adjust the declaration of the `data128` member above so it is always defined if it's " \
+ "supported by the current compiler/architecture in any configuration."
+#endif
+
+ constexpr explicit operator QByteArrayView() const noexcept
+ {
+ return QByteArrayView(data, sizeof(data));
+ }
+
+ friend constexpr Id128Bytes qbswap(Id128Bytes b) noexcept
+ {
+ // 128-bit byte swap
+ auto b0 = qbswap(b.data64[0]);
+ auto b1 = qbswap(b.data64[1]);
+ b.data64[0] = b1;
+ b.data64[1] = b0;
+ return b;
+ }
+ };
+
constexpr QUuid() noexcept : data1(0), data2(0), data3(0), data4{0,0,0,0,0,0,0,0} {}
constexpr QUuid(uint l, ushort w1, ushort w2, uchar b1, uchar b2, uchar b3,
uchar b4, uchar b5, uchar b6, uchar b7, uchar b8) noexcept
: data1(l), data2(w1), data3(w2), data4{b1, b2, b3, b4, b5, b6, b7, b8} {}
+ explicit inline QUuid(Id128Bytes id128, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept;
explicit QUuid(QAnyStringView string) noexcept
: QUuid{fromString(string)} {}
@@ -73,34 +107,76 @@ public:
#endif
QString toString(StringFormat mode = WithBraces) const;
QByteArray toByteArray(StringFormat mode = WithBraces) const;
+ inline Id128Bytes toBytes(QSysInfo::Endian order = QSysInfo::BigEndian) const noexcept;
QByteArray toRfc4122() const;
+
+ static inline QUuid fromBytes(const void *bytes, QSysInfo::Endian order = QSysInfo::BigEndian);
#if QT_CORE_REMOVED_SINCE(6, 3)
static QUuid fromRfc4122(const QByteArray &);
#endif
static QUuid fromRfc4122(QByteArrayView) noexcept;
+
bool isNull() const noexcept;
- constexpr bool operator==(const QUuid &orig) const noexcept
+#ifdef QT_SUPPORTS_INT128
+ static constexpr QUuid fromUInt128(quint128 uuid, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept;
+ constexpr quint128 toUInt128(QSysInfo::Endian order = QSysInfo::BigEndian) const noexcept;
+#endif
+
+private:
+ friend constexpr bool comparesEqual(const QUuid &lhs, const QUuid &rhs) noexcept
{
- if (data1 != orig.data1 || data2 != orig.data2 ||
- data3 != orig.data3)
+ if (lhs.data1 != rhs.data1 || lhs.data2 != rhs.data2 || lhs.data3 != rhs.data3)
return false;
- for (uint i = 0; i < 8; i++)
- if (data4[i] != orig.data4[i])
+ for (uint i = 0; i < 8; i++) {
+ if (lhs.data4[i] != rhs.data4[i])
return false;
+ }
return true;
}
+ friend Qt::strong_ordering compareThreeWay(const QUuid &lhs, const QUuid &rhs) noexcept
+ {
+ if (lhs.variant() != rhs.variant())
+ return Qt::compareThreeWay(lhs.variant(), rhs.variant());
+ if (lhs.data1 != rhs.data1)
+ return Qt::compareThreeWay(lhs.data1, rhs.data1);
+ if (lhs.data2 != rhs.data2)
+ return Qt::compareThreeWay(lhs.data2, rhs.data2);
+ if (lhs.data3 != rhs.data3)
+ return Qt::compareThreeWay(lhs.data3, rhs.data3);
+
+ int c = std::memcmp(lhs.data4, rhs.data4, sizeof(lhs.data4));
+ return Qt::compareThreeWay(c, 0);
+ }
+
+public:
+/* To prevent a meta-type creation ambiguity on Windows, we put comparison
+ macros under NOT QT_CORE_REMOVED_SINCE(6, 8) part. */
+#if QT_CORE_REMOVED_SINCE(6, 8)
+ constexpr bool operator==(const QUuid &orig) const noexcept
+ {
+ return comparesEqual(*this, orig);
+ }
constexpr bool operator!=(const QUuid &orig) const noexcept
{
- return !(*this == orig);
+ return !operator==(orig);
}
bool operator<(const QUuid &other) const noexcept;
bool operator>(const QUuid &other) const noexcept;
-
+#else
+private:
+#if defined(__cpp_lib_three_way_comparison) && !defined(Q_QDOC)
+ QT_DECLARE_3WAY_HELPER_STRONG(QUuid, QUuid, /* non-constexpr */, /* no attributes */)
+#else
+ QT_DECLARE_ORDERING_HELPER_STRONG(QUuid, QUuid, /* non-constexpr */, /* no attributes */)
+#endif // defined(__cpp_lib_three_way_comparison) && !defined(Q_QDOC)
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QUuid)
+#endif // QT_CORE_REMOVED_SINCE(6, 8)
+public:
#if defined(Q_OS_WIN) || defined(Q_QDOC)
// On Windows we have a type GUID that is used by the platform API, so we
// provide convenience operators to cast from and to this type.
@@ -120,17 +196,29 @@ public:
GUID guid = { data1, data2, data3, { data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7] } };
return guid;
}
-
+private:
+ friend constexpr bool comparesEqual(const QUuid &lhs, const GUID &rhs) noexcept
+ {
+ return comparesEqual(lhs, QUuid(rhs));
+ }
+public:
+/* To prevent a meta-type creation ambiguity on Windows, we put comparison
+ macros under NOT QT_CORE_REMOVED_SINCE(6, 8) part. */
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr bool operator==(const GUID &guid) const noexcept
{
- return *this == QUuid(guid);
+ return comparesEqual(*this, QUuid(guid));
}
constexpr bool operator!=(const GUID &guid) const noexcept
{
- return !(*this == guid);
+ return !operator==(guid);
}
+#else
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QUuid, GUID)
+#endif // !QT_CORE_REMOVED_SINCE(6, 8)
#endif
+public:
static QUuid createUuid();
#ifndef QT_BOOTSTRAPPED
static QUuid createUuidV3(const QUuid &ns, const QByteArray &baseData);
@@ -177,10 +265,86 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QUuid &);
Q_CORE_EXPORT size_t qHash(const QUuid &uuid, size_t seed = 0) noexcept;
-inline bool operator<=(const QUuid &lhs, const QUuid &rhs) noexcept
-{ return !(rhs < lhs); }
-inline bool operator>=(const QUuid &lhs, const QUuid &rhs) noexcept
-{ return !(lhs < rhs); }
+QUuid::QUuid(Id128Bytes uuid, QSysInfo::Endian order) noexcept
+{
+ if (order == QSysInfo::LittleEndian)
+ uuid = qbswap(uuid);
+ data1 = qFromBigEndian<quint32>(&uuid.data[0]);
+ data2 = qFromBigEndian<quint16>(&uuid.data[4]);
+ data3 = qFromBigEndian<quint16>(&uuid.data[6]);
+ memcpy(data4, &uuid.data[8], sizeof(data4));
+}
+
+QUuid::Id128Bytes QUuid::toBytes(QSysInfo::Endian order) const noexcept
+{
+ Id128Bytes result = {};
+ qToBigEndian(data1, &result.data[0]);
+ qToBigEndian(data2, &result.data[4]);
+ qToBigEndian(data3, &result.data[6]);
+ memcpy(&result.data[8], data4, sizeof(data4));
+ if (order == QSysInfo::LittleEndian)
+ return qbswap(result);
+ return result;
+}
+
+QUuid QUuid::fromBytes(const void *bytes, QSysInfo::Endian order)
+{
+ Id128Bytes result = {};
+ memcpy(result.data, bytes, sizeof(result));
+ return QUuid(result, order);
+}
+
+#ifdef QT_SUPPORTS_INT128
+constexpr QUuid QUuid::fromUInt128(quint128 uuid, QSysInfo::Endian order) noexcept
+{
+ QUuid result = {};
+ if (order == QSysInfo::BigEndian) {
+ result.data1 = qFromBigEndian<quint32>(int(uuid));
+ result.data2 = qFromBigEndian<quint16>(ushort(uuid >> 32));
+ result.data3 = qFromBigEndian<quint16>(ushort(uuid >> 48));
+ for (int i = 0; i < 8; ++i)
+ result.data4[i] = uchar(uuid >> (64 + i * 8));
+ } else {
+ result.data1 = qFromLittleEndian<quint32>(uint(uuid >> 96));
+ result.data2 = qFromLittleEndian<quint16>(ushort(uuid >> 80));
+ result.data3 = qFromLittleEndian<quint16>(ushort(uuid >> 64));
+ for (int i = 0; i < 8; ++i)
+ result.data4[i] = uchar(uuid >> (56 - i * 8));
+ }
+ return result;
+}
+
+constexpr quint128 QUuid::toUInt128(QSysInfo::Endian order) const noexcept
+{
+ quint128 result = {};
+ if (order == QSysInfo::BigEndian) {
+ for (int i = 0; i < 8; ++i)
+ result |= quint64(data4[i]) << (i * 8);
+ result = result << 64;
+ result |= quint64(qToBigEndian<quint16>(data3)) << 48;
+ result |= quint64(qToBigEndian<quint16>(data2)) << 32;
+ result |= qToBigEndian<quint32>(data1);
+ } else {
+ result = qToLittleEndian<quint32>(data1);
+ result = result << 32;
+ result |= quint64(qToLittleEndian<quint16>(data2)) << 16;
+ result |= quint64(qToLittleEndian<quint16>(data3));
+ result = result << 64;
+ for (int i = 0; i < 8; ++i)
+ result |= quint64(data4[i]) << (56 - i * 8);
+ }
+ return result;
+}
+#endif
+
+#if defined(Q_QDOC)
+// provide fake declarations of qXXXEndian() functions, so that qDoc could
+// distinguish them from the general template
+QUuid::Id128Bytes qFromBigEndian(QUuid::Id128Bytes src);
+QUuid::Id128Bytes qFromLittleEndian(QUuid::Id128Bytes src);
+QUuid::Id128Bytes qToBigEndian(QUuid::Id128Bytes src);
+QUuid::Id128Bytes qToLittleEndian(QUuid::Id128Bytes src);
+#endif
QT_END_NAMESPACE