summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/CMakeLists.txt7
-rw-r--r--src/corelib/configure.cmake27
-rw-r--r--src/corelib/doc/snippets/code/doc_src_properties.cpp2
-rw-r--r--src/corelib/doc/src/objectmodel/properties.qdoc16
-rw-r--r--src/corelib/global/qnumeric.h21
-rw-r--r--src/corelib/io/qioring.cpp30
-rw-r--r--src/corelib/io/qioring_linux.cpp13
-rw-r--r--src/corelib/io/qioring_p.h39
-rw-r--r--src/corelib/io/qioring_win.cpp754
-rw-r--r--src/corelib/itemmodels/qrangemodel.cpp1
-rw-r--r--src/corelib/itemmodels/qrangemodel_impl.h209
-rw-r--r--src/corelib/itemmodels/qrangemodeladapter.qdoc8
-rw-r--r--src/corelib/kernel/qmetaobject.cpp55
-rw-r--r--src/corelib/kernel/qmetaobject.h2
-rw-r--r--src/corelib/kernel/qmetaobject_p.h1
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp55
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder_p.h4
-rw-r--r--src/corelib/kernel/qobjectdefs.h2
-rw-r--r--src/corelib/kernel/qtmocconstants.h6
-rw-r--r--src/corelib/kernel/qtmochelpers.h7
-rw-r--r--src/corelib/mimetypes/qmimeprovider.cpp7
-rw-r--r--src/corelib/tools/qflatmap_p.h15
-rw-r--r--src/corelib/tools/qmargins.h15
-rw-r--r--src/corelib/tools/qpoint.h7
-rw-r--r--src/corelib/tools/qsize.h5
25 files changed, 1132 insertions, 176 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index dec68c5f9f4..ea8cf7b9c8e 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -583,7 +583,7 @@ if(QT_FEATURE_async_io)
SOURCES
io/qrandomaccessasyncfile_darwin.mm
)
- elseif(LINUX AND QT_FEATURE_liburing)
+ elseif((LINUX AND QT_FEATURE_liburing) OR (WIN32 AND QT_FEATURE_windows_ioring))
qt_internal_extend_target(Core
SOURCES
io/qrandomaccessasyncfile_qioring.cpp
@@ -763,6 +763,11 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_liburing
uring
)
+qt_internal_extend_target(Core CONDITION QT_FEATURE_windows_ioring
+ SOURCES
+ io/qioring.cpp io/qioring_win.cpp io/qioring_p.h
+)
+
# Workaround for QTBUG-101411
# Remove if QCC (gcc version 8.3.0) for QNX 7.1.0 is no longer supported
qt_internal_extend_target(Core CONDITION QCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "8.3.0")
diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake
index c1d15c75054..7216f2920fe 100644
--- a/src/corelib/configure.cmake
+++ b/src/corelib/configure.cmake
@@ -605,6 +605,27 @@ int main(void)
"
)
+qt_config_compile_test(windows_ioring
+ LABEL "Windows SDK: IORing"
+ CODE
+"#include <windows.h>
+#include <ioringapi.h>
+
+int main(void)
+{
+ /* BEGIN TEST: */
+ IORING_CREATE_FLAGS flags;
+ memset(&flags, 0, sizeof(flags));
+ HIORING ioRingHandle = nullptr;
+ HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, 1, 1, &ioRingHandle);
+ if (hr == IORING_E_SUBMISSION_QUEUE_FULL) // not valid, but test that this #define exists
+ return 0;
+ /* END TEST: */
+ return 0;
+}
+"
+)
+
# cpp_winrt
qt_config_compile_test(cpp_winrt
LABEL "cpp/winrt"
@@ -785,6 +806,11 @@ qt_feature("winsdkicu" PRIVATE
CONDITION TEST_winsdkicu
DISABLE QT_FEATURE_icu
)
+qt_feature("windows_ioring" PRIVATE
+ LABEL "Windows I/O Ring"
+ AUTODETECT WIN32 AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 10.0.22000
+ CONDITION TEST_windows_ioring
+)
qt_feature("inotify" PUBLIC PRIVATE
LABEL "inotify"
CONDITION TEST_inotify OR TEST_fsnotify
@@ -1272,6 +1298,7 @@ qt_configure_add_summary_entry(ARGS "glib")
qt_configure_add_summary_entry(ARGS "icu")
qt_configure_add_summary_entry(ARGS "jemalloc")
qt_configure_add_summary_entry(ARGS "liburing")
+qt_configure_add_summary_entry(ARGS "windows_ioring")
qt_configure_add_summary_entry(ARGS "timezone_tzdb")
qt_configure_add_summary_entry(ARGS "system-libb2")
qt_configure_add_summary_entry(ARGS "mimetype-database")
diff --git a/src/corelib/doc/snippets/code/doc_src_properties.cpp b/src/corelib/doc/snippets/code/doc_src_properties.cpp
index eafa7acda3b..07f574c2de2 100644
--- a/src/corelib/doc/snippets/code/doc_src_properties.cpp
+++ b/src/corelib/doc/snippets/code/doc_src_properties.cpp
@@ -16,6 +16,8 @@ Q_PROPERTY(type name
[BINDABLE bindableProperty]
[CONSTANT]
[FINAL]
+ [VIRTUAL]
+ [OVERRIDE]
[REQUIRED])
//! [0]
diff --git a/src/corelib/doc/src/objectmodel/properties.qdoc b/src/corelib/doc/src/objectmodel/properties.qdoc
index 0e66c8445c2..71e14222763 100644
--- a/src/corelib/doc/src/objectmodel/properties.qdoc
+++ b/src/corelib/doc/src/objectmodel/properties.qdoc
@@ -128,10 +128,18 @@
constant value may be different for different instances of the object. A
constant property cannot have a WRITE method or a NOTIFY signal.
- \li The presence of the \c FINAL attribute indicates that the property
- will not be overridden by a derived class. This can be used for performance
- optimizations in some cases, but is not enforced by moc. Care must be taken
- never to override a \c FINAL property.
+ \li \c FINAL, \c VIRTUAL, \c OVERRIDE modifiers mirror the semantics of their C++ and
+ \l {Override Semantics}{QML counterparts}, allowing to make property overriding explicit at the
+ meta-object level.
+
+ \note At present, these modifiers are not enforced by moc.
+ They are recognized syntactically and are primarily used for QML runtime enforcement and tooling
+ diagnostics. Future versions may introduce stricter compile-time validation and warnings for
+ invalid overrides across modules.
+
+ \note If you want to change accessing behaviour for a property, use the
+ polymorphism provided by C++.
+
\li The presence of the \c REQUIRED attribute indicates that the property
should be set by a user of the class. This is not enforced by moc, and is
diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h
index 48e736ff124..4568d089590 100644
--- a/src/corelib/global/qnumeric.h
+++ b/src/corelib/global/qnumeric.h
@@ -627,6 +627,27 @@ QT_WARNING_DISABLE_FLOAT_COMPARE
QT_WARNING_POP
+namespace QtPrivate {
+/*
+ A version of qFuzzyCompare that works for all values (qFuzzyCompare()
+ requires that neither argument is numerically 0).
+
+ It's private because we need a fix for the many qFuzzyCompare() uses that
+ ignore the precondition, even for older branches.
+
+ See QTBUG-142020 for discussion of a longer-term solution.
+*/
+template <typename T, typename S>
+[[nodiscard]] constexpr bool fuzzyCompare(const T &lhs, const S &rhs) noexcept
+{
+ static_assert(noexcept(qIsNull(lhs) && qIsNull(rhs) && qFuzzyIsNull(lhs - rhs) && qFuzzyCompare(lhs, rhs)),
+ "The operations qIsNull(), qFuzzyIsNull() and qFuzzyCompare() must be noexcept"
+ "for both argument types!");
+ return qIsNull(lhs) || qIsNull(rhs) ? qFuzzyIsNull(lhs - rhs) : qFuzzyCompare(lhs, rhs);
+}
+} // namespace QtPrivate
+
+
inline int qIntCast(double f) { return int(f); }
inline int qIntCast(float f) { return int(f); }
diff --git a/src/corelib/io/qioring.cpp b/src/corelib/io/qioring.cpp
index 28849b49b04..2eb013e24fc 100644
--- a/src/corelib/io/qioring.cpp
+++ b/src/corelib/io/qioring.cpp
@@ -8,6 +8,20 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQIORing, "qt.core.ioring", QtCriticalMsg)
+QIORing *QIORing::sharedInstance()
+{
+ thread_local QIORing instance;
+ if (!instance.initializeIORing())
+ return nullptr;
+ return &instance;
+}
+
+QIORing::QIORing(quint32 submissionQueueSize, quint32 completionQueueSize)
+ : sqEntries(submissionQueueSize), cqEntries(completionQueueSize)
+{
+ // Destructor in respective _<platform>.cpp
+}
+
auto QIORing::queueRequestInternal(GenericRequestType &request) -> QueuedRequestStatus
{
if (!ensureInitialized() || preparingRequests) { // preparingRequests protects against recursing
@@ -65,12 +79,20 @@ template <typename T>
constexpr bool HasResultMember = qxp::is_detected_v<DetectResult, T>;
}
+void QIORing::setFileErrorResult(QIORing::GenericRequestType &req, QFileDevice::FileError error)
+{
+ invokeOnOp(req, [error](auto *concreteRequest) {
+ if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>)
+ setFileErrorResult(*concreteRequest, error);
+ });
+}
+
void QIORing::finishRequestWithError(QIORing::GenericRequestType &req, QFileDevice::FileError error)
{
- invokeOnOp(req, [error](auto *req) {
- if constexpr (QtPrivate::HasResultMember<decltype(*req)>)
- req->result.template emplace<QFileDevice::FileError>(error);
- invokeCallback(*req);
+ invokeOnOp(req, [error](auto *concreteRequest) {
+ if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>)
+ setFileErrorResult(*concreteRequest, error);
+ invokeCallback(*concreteRequest);
});
}
diff --git a/src/corelib/io/qioring_linux.cpp b/src/corelib/io/qioring_linux.cpp
index b296b916c81..2b5865f3c2d 100644
--- a/src/corelib/io/qioring_linux.cpp
+++ b/src/corelib/io/qioring_linux.cpp
@@ -35,19 +35,6 @@ static io_uring_op toUringOp(QIORing::Operation op);
static void prepareFileReadWrite(io_uring_sqe *sqe, const QIORingRequestOffsetFdBase &request,
const void *address, qsizetype size);
-
-QIORing *QIORing::sharedInstance()
-{
- thread_local QIORing instance;
- if (!instance.initializeIORing())
- return nullptr;
- return &instance;
-}
-
-QIORing::QIORing(quint32 submissionQueueSize, quint32 completionQueueSize)
- : sqEntries(submissionQueueSize), cqEntries(completionQueueSize)
-{
-}
QIORing::~QIORing()
{
if (eventDescriptor != -1)
diff --git a/src/corelib/io/qioring_p.h b/src/corelib/io/qioring_p.h
index d4c4308122e..0db832bc6bf 100644
--- a/src/corelib/io/qioring_p.h
+++ b/src/corelib/io/qioring_p.h
@@ -22,7 +22,6 @@
#include <QtCore/qspan.h>
#include <QtCore/qhash.h>
#include <QtCore/qfiledevice.h>
-#include <QtCore/qwineventnotifier.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qdeadlinetimer.h>
@@ -30,10 +29,15 @@
# include <QtCore/qsocketnotifier.h>
struct io_uring_sqe;
struct io_uring_cqe;
+#elif defined(Q_OS_WIN)
+# include <QtCore/qwineventnotifier.h>
+# include <qt_windows.h>
+# include <ioringapi.h>
#endif
#include <algorithm>
#include <filesystem>
+#include <QtCore/qxpfunctional.h>
#include <variant>
#include <optional>
#include <type_traits>
@@ -162,6 +166,12 @@ private:
template <typename Fun>
static auto invokeOnOp(GenericRequestType &req, Fun fn);
+ template <Operation Op>
+ static void setFileErrorResult(QIORingRequest<Op> &req, QFileDevice::FileError error)
+ {
+ req.result.template emplace<QFileDevice::FileError>(error);
+ }
+ static void setFileErrorResult(GenericRequestType &req, QFileDevice::FileError error);
static void finishRequestWithError(GenericRequestType &req, QFileDevice::FileError error);
static bool verifyFd(GenericRequestType &req);
@@ -205,6 +215,28 @@ private:
ReadWriteStatus handleReadCompletion(const io_uring_cqe *cqe, GenericRequestType *request);
template <Operation Op>
ReadWriteStatus handleWriteCompletion(const io_uring_cqe *cqe, GenericRequestType *request);
+#elif defined(Q_OS_WIN)
+ // We use UINT32 because that's the type used for size parameters in their API.
+ static constexpr qsizetype MaxReadWriteLen = std::numeric_limits<UINT32>::max();
+ std::optional<QWinEventNotifier> notifier;
+ HIORING ioRingHandle = nullptr;
+ HANDLE eventHandle = INVALID_HANDLE_VALUE;
+
+ bool initialized = false;
+ bool queueWasFull = false;
+ [[nodiscard]]
+ RequestPrepResult prepareRequest(GenericRequestType &request);
+ QIORing::ReadWriteStatus handleReadCompletion(
+ HRESULT result, quintptr information, QSpan<std::byte> *destinations, void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResult);
+ template <Operation Op>
+ ReadWriteStatus handleReadCompletion(const IORING_CQE *cqe, GenericRequestType *request);
+ ReadWriteStatus handleWriteCompletion(
+ HRESULT result, quintptr information, const QSpan<const std::byte> *sources,
+ void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResult);
+ template <Operation Op>
+ ReadWriteStatus handleWriteCompletion(const IORING_CQE *cqe, GenericRequestType *request);
#endif
};
@@ -243,6 +275,7 @@ struct QIORingRequestBase : Base
template <>
struct QIORingResult<QtPrivate::Operation::Open>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
template <>
@@ -260,6 +293,7 @@ template <>
struct QIORingRequest<QtPrivate::Operation::Close> final
: QIORingRequestBase<QtPrivate::Operation::Close, QIORingRequestEmptyBase>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
@@ -318,6 +352,7 @@ struct QIORingResult<QtPrivate::Operation::Flush> final
template <>
struct QIORingRequest<QtPrivate::Operation::Flush> final : QIORingRequestBase<QtPrivate::Operation::Flush, QIORingRequestEmptyBase>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
@@ -330,6 +365,7 @@ template <>
struct QIORingRequest<QtPrivate::Operation::Stat> final
: QIORingRequestBase<QtPrivate::Operation::Stat, QIORingRequestEmptyBase>
{
+ // On Windows this is a HANDLE
qintptr fd;
};
@@ -473,6 +509,7 @@ namespace QtPrivate {
// The 'extra' struct for Read/Write operations that must be split up
struct ReadWriteExtra
{
+ qint64 totalProcessed = 0;
qsizetype spanIndex = 0;
qsizetype spanOffset = 0;
qsizetype numSpans = 1;
diff --git a/src/corelib/io/qioring_win.cpp b/src/corelib/io/qioring_win.cpp
new file mode 100644
index 00000000000..42c51f428d6
--- /dev/null
+++ b/src/corelib/io/qioring_win.cpp
@@ -0,0 +1,754 @@
+// Copyright (C) 2025 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// Qt-Security score:significant reason:default
+
+#include "qioring_p.h"
+
+QT_REQUIRE_CONFIG(windows_ioring);
+
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qscopedvaluerollback.h>
+
+#include <qt_windows.h>
+#include <ioringapi.h>
+
+#include <QtCore/q26numeric.h>
+
+QT_BEGIN_NAMESPACE
+
+// We don't really build for 32-bit windows anymore, but this code is definitely wrong if someone
+// does.
+static_assert(sizeof(qsizetype) > sizeof(UINT32),
+ "This code is written with assuming 64-bit Windows.");
+
+using namespace Qt::StringLiterals;
+
+static HRESULT buildReadOperation(HIORING ioRingHandle, qintptr fd, QSpan<std::byte> destination,
+ quint64 offset, quintptr userData)
+{
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef((HANDLE(fd)));
+ const IORING_BUFFER_REF bufferRef(destination.data());
+ const auto maxSize = q26::saturate_cast<UINT32>(destination.size());
+ Q_ASSERT(maxSize == destination.size());
+ return BuildIoRingReadFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, userData,
+ IOSQE_FLAGS_NONE);
+}
+
+static HRESULT buildWriteOperation(HIORING ioRingHandle, qintptr fd, QSpan<const std::byte> source,
+ quint64 offset, quintptr userData)
+{
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef((HANDLE(fd)));
+ const IORING_BUFFER_REF bufferRef(const_cast<std::byte *>(source.data()));
+ const auto maxSize = q26::saturate_cast<UINT32>(source.size());
+ Q_ASSERT(maxSize == source.size());
+ // @todo: FILE_WRITE_FLAGS can be set to write-through, could be used for Unbuffered mode.
+ return BuildIoRingWriteFile(ioRingHandle, fileRef, bufferRef, maxSize, offset,
+ FILE_WRITE_FLAGS_NONE, userData, IOSQE_FLAGS_NONE);
+}
+
+QIORing::~QIORing()
+{
+ if (initialized) {
+ CloseHandle(eventHandle);
+ CloseIoRing(ioRingHandle);
+ }
+}
+
+bool QIORing::initializeIORing()
+{
+ if (initialized)
+ return true;
+
+ IORING_CAPABILITIES capabilities;
+ QueryIoRingCapabilities(&capabilities);
+ if (capabilities.MaxVersion < IORING_VERSION_3) // 3 adds write, flush and drain
+ return false;
+ if ((capabilities.FeatureFlags & IORING_FEATURE_SET_COMPLETION_EVENT) == 0)
+ return false; // We currently require the SET_COMPLETION_EVENT feature
+
+ qCDebug(lcQIORing) << "Creating QIORing, requesting space for" << sqEntries
+ << "submission queue entries, and" << cqEntries
+ << "completion queue entries";
+
+ IORING_CREATE_FLAGS flags;
+ memset(&flags, 0, sizeof(flags));
+ HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, sqEntries, cqEntries, &ioRingHandle);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "failed to initialize QIORing");
+ return false;
+ }
+ auto earlyExitCleanup = qScopeGuard([this]() {
+ if (eventHandle != INVALID_HANDLE_VALUE)
+ CloseHandle(eventHandle);
+ CloseIoRing(ioRingHandle);
+ });
+ eventHandle = CreateEvent(nullptr, TRUE, FALSE, nullptr);
+ if (eventHandle == INVALID_HANDLE_VALUE) {
+ qErrnoWarning("Failed to create event handle");
+ return false;
+ }
+ notifier.emplace(eventHandle);
+ hr = SetIoRingCompletionEvent(ioRingHandle, eventHandle);
+ if (FAILED(hr)) {
+ qErrnoWarning(hr, "Failed to assign the event handle to QIORing");
+ return false;
+ }
+ IORING_INFO info;
+ if (SUCCEEDED(GetIoRingInfo(ioRingHandle, &info))) {
+ sqEntries = info.SubmissionQueueSize;
+ cqEntries = info.CompletionQueueSize;
+ qCDebug(lcQIORing) << "QIORing configured with capacity for" << sqEntries
+ << "submissions, and" << cqEntries << "completions.";
+ }
+ QObject::connect(std::addressof(*notifier), &QWinEventNotifier::activated,
+ std::addressof(*notifier), [this]() { completionReady(); });
+ initialized = true;
+ earlyExitCleanup.dismiss();
+ return true;
+}
+
+QIORing::ReadWriteStatus QIORing::handleReadCompletion(
+ HRESULT result, quintptr information, QSpan<std::byte> *destinations, void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResultFn)
+{
+ if (FAILED(result)) {
+ if (result == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF))
+ return ReadWriteStatus::Finished;
+
+ if (result == E_ABORT)
+ setResultFn(QFileDevice::AbortError);
+ else
+ setResultFn(QFileDevice::ReadError);
+ } else if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) {
+ const qsizetype bytesRead = q26::saturate_cast<decltype(MaxReadWriteLen)>(information);
+ qCDebug(lcQIORing) << "Partial read of" << bytesRead << "bytes completed";
+ extra->totalProcessed = setResultFn(bytesRead);
+ extra->spanOffset += bytesRead;
+ qCDebug(lcQIORing) << "Read operation progress: span" << extra->spanIndex << "offset"
+ << extra->spanOffset << "of" << destinations[extra->spanIndex].size()
+ << "bytes. Total read:" << extra->totalProcessed << "bytes";
+ // The while loop is in case there is an empty span, we skip over it:
+ while (extra->spanOffset == destinations[extra->spanIndex].size()) {
+ // Move to next span
+ if (++extra->spanIndex == extra->numSpans)
+ return ReadWriteStatus::Finished;
+ extra->spanOffset = 0;
+ }
+ return ReadWriteStatus::MoreToDo;
+ } else {
+ setResultFn(q26::saturate_cast<decltype(MaxReadWriteLen)>(information));
+ }
+ return ReadWriteStatus::Finished;
+}
+
+template <QIORing::Operation Op>
+Q_ALWAYS_INLINE QIORing::ReadWriteStatus QIORing::handleReadCompletion(const IORING_CQE *cqe,
+ GenericRequestType *request)
+{
+ static_assert(Op == Operation::Read || Op == Operation::VectoredRead);
+ QIORingRequest<Op> *readRequest = request->requestData<Op>();
+ Q_ASSERT(readRequest);
+ auto *destinations = [&readRequest]() {
+ if constexpr (Op == Operation::Read)
+ return &readRequest->destination;
+ else
+ return &readRequest->destinations[0];
+ }();
+ auto setResult = [readRequest](const std::variant<QFileDevice::FileError, qint64> &result) {
+ if (result.index() == 0) { // error
+ QIORing::setFileErrorResult(*readRequest, *std::get_if<QFileDevice::FileError>(&result));
+ return 0ll;
+ }
+ // else: success
+ auto &readResult = [&readRequest]() -> QIORingResult<Op> & {
+ if (auto *result = std::get_if<QIORingResult<Op>>(&readRequest->result))
+ return *result;
+ return readRequest->result.template emplace<QIORingResult<Op>>();
+ }();
+ qint64 bytesRead = *std::get_if<qint64>(&result);
+ readResult.bytesRead += bytesRead;
+ return readResult.bytesRead;
+ };
+ QIORing::ReadWriteStatus rwstatus = handleReadCompletion(
+ cqe->ResultCode, cqe->Information, destinations, request->getExtra<void>(), setResult);
+ switch (rwstatus) {
+ case ReadWriteStatus::Finished:
+ if (request->getExtra<void>())
+ --ongoingSplitOperations;
+ break;
+ case ReadWriteStatus::MoreToDo: {
+ // Move the request such that it is next in the list to be processed:
+ auto &it = addrItMap[request];
+ const auto where = lastUnqueuedIterator.value_or(pendingRequests.end());
+ pendingRequests.splice(where, pendingRequests, it);
+ it = std::prev(where);
+ lastUnqueuedIterator = it;
+ break;
+ }
+ }
+ return rwstatus;
+}
+
+QIORing::ReadWriteStatus QIORing::handleWriteCompletion(
+ HRESULT result, quintptr information, const QSpan<const std::byte> *sources, void *voidExtra,
+ qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResultFn)
+{
+ if (FAILED(result)) {
+ if (result == E_ABORT)
+ setResultFn(QFileDevice::AbortError);
+ else
+ setResultFn(QFileDevice::WriteError);
+ } else if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) {
+ const qsizetype bytesWritten = q26::saturate_cast<decltype(MaxReadWriteLen)>(information);
+ qCDebug(lcQIORing) << "Partial write of" << bytesWritten << "bytes completed";
+ extra->totalProcessed = setResultFn(bytesWritten);
+ extra->spanOffset += bytesWritten;
+ qCDebug(lcQIORing) << "Write operation progress: span" << extra->spanIndex << "offset"
+ << extra->spanOffset << "of" << sources[extra->spanIndex].size()
+ << "bytes. Total written:" << extra->totalProcessed << "bytes";
+ // The while loop is in case there is an empty span, we skip over it:
+ while (extra->spanOffset == sources[extra->spanIndex].size()) {
+ // Move to next span
+ if (++extra->spanIndex == extra->numSpans)
+ return ReadWriteStatus::Finished;
+ extra->spanOffset = 0;
+ }
+ return ReadWriteStatus::MoreToDo;
+ } else {
+ setResultFn(q26::saturate_cast<decltype(MaxReadWriteLen)>(information));
+ }
+ return ReadWriteStatus::Finished;
+}
+
+template <QIORing::Operation Op>
+Q_ALWAYS_INLINE QIORing::ReadWriteStatus QIORing::handleWriteCompletion(const IORING_CQE *cqe,
+ GenericRequestType *request)
+{
+ static_assert(Op == Operation::Write || Op == Operation::VectoredWrite);
+ QIORingRequest<Op> *writeRequest = request->requestData<Op>();
+ auto *sources = [&writeRequest]() {
+ if constexpr (Op == Operation::Write)
+ return &writeRequest->source;
+ else
+ return &writeRequest->sources[0];
+ }();
+ auto setResult = [writeRequest](const std::variant<QFileDevice::FileError, qint64> &result) {
+ if (result.index() == 0) { // error
+ QIORing::setFileErrorResult(*writeRequest, *std::get_if<QFileDevice::FileError>(&result));
+ return 0ll;
+ }
+ // else: success
+ auto &writeResult = [&writeRequest]() -> QIORingResult<Op> & {
+ if (auto *result = std::get_if<QIORingResult<Op>>(&writeRequest->result))
+ return *result;
+ return writeRequest->result.template emplace<QIORingResult<Op>>();
+ }();
+ qint64 bytesWritten = *std::get_if<qint64>(&result);
+ writeResult.bytesWritten += bytesWritten;
+ return writeResult.bytesWritten;
+ };
+ QIORing::ReadWriteStatus rwstatus = handleWriteCompletion(
+ cqe->ResultCode, cqe->Information, sources, request->getExtra<void>(), setResult);
+ switch (rwstatus) {
+ case ReadWriteStatus::Finished:
+ if (request->getExtra<void>())
+ --ongoingSplitOperations;
+ break;
+ case ReadWriteStatus::MoreToDo: {
+ // Move the request such that it is next in the list to be processed:
+ auto &it = addrItMap[request];
+ const auto where = lastUnqueuedIterator.value_or(pendingRequests.end());
+ pendingRequests.splice(where, pendingRequests, it);
+ it = std::prev(where);
+ lastUnqueuedIterator = it;
+ break;
+ }
+ }
+ return rwstatus;
+}
+
+void QIORing::completionReady()
+{
+ ResetEvent(eventHandle);
+ IORING_CQE entry;
+ while (PopIoRingCompletion(ioRingHandle, &entry) == S_OK) {
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ auto *request = reinterpret_cast<GenericRequestType *>(entry.UserData);
+ if (!addrItMap.contains(request)) {
+ qCDebug(lcQIORing) << "Got completed entry, but cannot find it in the map. Likely "
+ "deleted, ignoring. UserData pointer:"
+ << request;
+ continue;
+ }
+ qCDebug(lcQIORing) << "Got completed entry. Operation:" << request->operation()
+ << "- UserData pointer:" << request
+ << "- Result:" << qt_error_string(entry.ResultCode) << '('
+ << QByteArray("0x"_ba + QByteArray::number(entry.ResultCode, 16)).data()
+ << ')';
+ switch (request->operation()) {
+ case Operation::Open: // Synchronously finishes
+ Q_UNREACHABLE_RETURN();
+ case Operation::Close: {
+ auto closeRequest = request->takeRequestData<Operation::Close>();
+ // We ignore the result of the flush, we are closing the handle anyway.
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ if (CloseHandle(HANDLE(closeRequest.fd)))
+ closeRequest.result.emplace<QIORingResult<Operation::Close>>();
+ else
+ closeRequest.result.emplace<QFileDevice::FileError>(QFileDevice::OpenError);
+ invokeCallback(closeRequest);
+ break;
+ }
+ case Operation::Read: {
+ const ReadWriteStatus status = handleReadCompletion<Operation::Read>(&entry, request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto readRequest = request->takeRequestData<Operation::Read>();
+ invokeCallback(readRequest);
+ break;
+ }
+ case Operation::Write: {
+ const ReadWriteStatus status = handleWriteCompletion<Operation::Write>(&entry, request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto writeRequest = request->takeRequestData<Operation::Write>();
+ invokeCallback(writeRequest);
+ break;
+ }
+ case Operation::VectoredRead: {
+ const ReadWriteStatus status = handleReadCompletion<Operation::VectoredRead>(&entry,
+ request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto vectoredReadRequest = request->takeRequestData<Operation::VectoredRead>();
+ invokeCallback(vectoredReadRequest);
+ break;
+ }
+ case Operation::VectoredWrite: {
+ const ReadWriteStatus status = handleWriteCompletion<Operation::VectoredWrite>(&entry,
+ request);
+ if (status == ReadWriteStatus::MoreToDo)
+ continue;
+ auto vectoredWriteRequest = request->takeRequestData<Operation::VectoredWrite>();
+ invokeCallback(vectoredWriteRequest);
+ break;
+ }
+ case Operation::Flush: {
+ auto flushRequest = request->takeRequestData<Operation::Flush>();
+ if (FAILED(entry.ResultCode)) {
+ qErrnoWarning(entry.ResultCode, "Flush operation failed");
+ // @todo any FlushError?
+ flushRequest.result.emplace<QFileDevice::FileError>(
+ QFileDevice::FileError::WriteError);
+ } else {
+ flushRequest.result.emplace<QIORingResult<Operation::Flush>>();
+ }
+ invokeCallback(flushRequest);
+ break;
+ }
+ case QtPrivate::Operation::Cancel: {
+ auto cancelRequest = request->takeRequestData<Operation::Cancel>();
+ invokeCallback(cancelRequest);
+ break;
+ }
+ case QtPrivate::Operation::Stat:
+ Q_UNREACHABLE_RETURN(); // Completes synchronously
+ break;
+ case Operation::NumOperations:
+ Q_UNREACHABLE_RETURN();
+ break;
+ }
+ auto it = addrItMap.take(request);
+ pendingRequests.erase(it);
+ --inFlightRequests;
+ queueWasFull = false;
+ }
+ prepareRequests();
+ if (unstagedRequests > 0)
+ submitRequests();
+}
+
+bool QIORing::waitForCompletions(QDeadlineTimer deadline)
+{
+ notifier->setEnabled(false);
+ auto reactivateNotifier = qScopeGuard([this]() {
+ notifier->setEnabled(true);
+ });
+
+ while (!deadline.hasExpired()) {
+ DWORD timeout = 0;
+ if (deadline.isForever()) {
+ timeout = INFINITE;
+ } else {
+ timeout = q26::saturate_cast<DWORD>(deadline.remainingTime());
+ if (timeout == INFINITE)
+ --timeout;
+ }
+ if (WaitForSingleObject(eventHandle, timeout) == WAIT_OBJECT_0)
+ return true;
+ }
+ return false;
+}
+
+static HANDLE openFile(const QIORingRequest<QIORing::Operation::Open> &openRequest)
+{
+ DWORD access = 0;
+ if (openRequest.flags.testFlag(QIODevice::ReadOnly))
+ access |= GENERIC_READ;
+ if (openRequest.flags.testFlag(QIODevice::WriteOnly))
+ access |= GENERIC_WRITE;
+
+ DWORD disposition = 0;
+ if (openRequest.flags.testFlag(QIODevice::Append)) {
+ qCWarning(lcQIORing, "Opening file with Append not supported for random access file");
+ return INVALID_HANDLE_VALUE;
+ }
+ if (openRequest.flags.testFlag(QIODevice::NewOnly)) {
+ disposition = CREATE_NEW;
+ } else {
+ // If Write is specified we _may_ create a file.
+ // See qfsfileengine_p.h openModeCanCreate.
+ disposition = openRequest.flags.testFlag(QIODeviceBase::WriteOnly)
+ && !openRequest.flags.testFlags(QIODeviceBase::ExistingOnly)
+ ? OPEN_ALWAYS
+ : OPEN_EXISTING;
+ }
+ const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ const DWORD flagsAndAttribs = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED;
+ HANDLE h = CreateFile(openRequest.path.native().c_str(), access, shareMode, nullptr,
+ disposition, flagsAndAttribs, nullptr);
+ if (h != INVALID_HANDLE_VALUE && openRequest.flags.testFlag(QIODeviceBase::Truncate)) {
+ FILE_END_OF_FILE_INFO info;
+ memset(&info, 0, sizeof(info));
+ SetFileInformationByHandle(h, FileEndOfFileInfo, &info, sizeof(info));
+ }
+ return h;
+}
+
+bool QIORing::supportsOperation(Operation op)
+{
+ switch (op) {
+ case QtPrivate::Operation::Open:
+ case QtPrivate::Operation::Close:
+ case QtPrivate::Operation::Read:
+ case QtPrivate::Operation::Write:
+ case QtPrivate::Operation::Flush:
+ case QtPrivate::Operation::Cancel:
+ case QtPrivate::Operation::Stat:
+ case QtPrivate::Operation::VectoredRead:
+ case QtPrivate::Operation::VectoredWrite:
+ return true;
+ case QtPrivate::Operation::NumOperations:
+ return false;
+ }
+ return false; // Not unreachable, we could allow more for io_uring
+}
+
+void QIORing::submitRequests()
+{
+ stagePending = false;
+ if (unstagedRequests == 0)
+ return;
+
+ // We perform a miniscule wait - to see if anything already in the queue is already completed -
+ // if we have been told the queue is full. Then we can try queuing more things right away
+ const bool shouldTryWait = std::exchange(queueWasFull, false);
+ const auto submitToRing = [this, &shouldTryWait] {
+ quint32 submittedEntries = 0;
+ HRESULT hr = SubmitIoRing(ioRingHandle, shouldTryWait ? 1 : 0, 1, &submittedEntries);
+ qCDebug(lcQIORing) << "Submitted" << submittedEntries << "requests";
+ unstagedRequests -= submittedEntries;
+ if (FAILED(hr)) {
+ // Too noisy, not a real problem
+ // qErrnoWarning(hr, "Failed to submit QIORing request: %u", submittedEntries);
+ return false;
+ }
+ return submittedEntries > 0;
+ };
+ if (submitToRing() && shouldTryWait) {
+ // We try to prepare some more request and submit more if able
+ prepareRequests();
+ if (unstagedRequests > 0)
+ submitToRing();
+ }
+}
+
+void QIORing::prepareRequests()
+{
+ if (!lastUnqueuedIterator)
+ return;
+ Q_ASSERT(!preparingRequests);
+ QScopedValueRollback<bool> prepareGuard(preparingRequests, true);
+
+ auto it = *lastUnqueuedIterator;
+ lastUnqueuedIterator.reset();
+ const auto end = pendingRequests.end();
+ while (!queueWasFull && it != end) {
+ auto &request = *it;
+ switch (prepareRequest(request)) {
+ case RequestPrepResult::Ok:
+ ++unstagedRequests;
+ ++inFlightRequests;
+ break;
+ case RequestPrepResult::QueueFull:
+ qCDebug(lcQIORing) << "Queue was reported as full, in flight requests:"
+ << inFlightRequests << "submission queue size:" << sqEntries
+ << "completion queue size:" << cqEntries;
+ queueWasFull = true;
+ lastUnqueuedIterator = it;
+ return;
+ case RequestPrepResult::Defer:
+ qCDebug(lcQIORing) << "Request for" << request.operation()
+ << "had to be deferred, will not queue any more requests at the "
+ "moment.";
+ lastUnqueuedIterator = it;
+ return; //
+ case RequestPrepResult::RequestCompleted:
+ // Used for requests that immediately finish. So we erase it:
+ qCDebug(lcQIORing) << "Request for" << request.operation()
+ << "completed synchronously.";
+ addrItMap.remove(&request);
+ it = pendingRequests.erase(it);
+ continue; // Don't increment iterator again
+ }
+ ++it;
+ }
+}
+
+namespace QtPrivate {
+template <typename T>
+using DetectHasFd = decltype(std::declval<const T &>().fd);
+
+template <typename T>
+constexpr bool OperationHasFd_v = qxp::is_detected_v<DetectHasFd, T>;
+} // namespace QtPrivate
+
+auto QIORing::prepareRequest(GenericRequestType &request) -> RequestPrepResult
+{
+ qCDebug(lcQIORing) << "Preparing a request with operation" << request.operation();
+ HRESULT hr = -1;
+
+ if (!verifyFd(request)) {
+ finishRequestWithError(request, QFileDevice::OpenError);
+ return RequestPrepResult::RequestCompleted;
+ }
+
+ switch (request.operation()) {
+ case Operation::Open: {
+ QIORingRequest<Operation::Open> openRequest = request.takeRequestData<Operation::Open>();
+ HANDLE fileDescriptor = openFile(openRequest);
+ if (fileDescriptor == INVALID_HANDLE_VALUE) {
+ openRequest.result.emplace<QFileDevice::FileError>(QFileDevice::FileError::OpenError);
+ } else {
+ auto &result = openRequest.result.emplace<QIORingResult<Operation::Open>>();
+ result.fd = qintptr(fileDescriptor);
+ }
+ invokeCallback(openRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ case Operation::Close: {
+ if (ongoingSplitOperations > 0)
+ return RequestPrepResult::Defer;
+
+ // We need to wait until all previous OPS are done before we close the request.
+ // There is no no-op request in the Windows QIORing, so we issue a flush.
+ auto *closeRequest = request.requestData<Operation::Close>();
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef(HANDLE(closeRequest->fd));
+ hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_MIN_METADATA,
+ quintptr(std::addressof(request)),
+ IOSQE_FLAGS_DRAIN_PRECEDING_OPS);
+ break;
+ }
+ case Operation::Read: {
+ auto *readRequest = request.requestData<Operation::Read>();
+ auto span = readRequest->destination;
+ auto offset = readRequest->offset;
+ if (span.size() > MaxReadWriteLen) {
+ qCDebug(lcQIORing) << "Requested Read of size" << span.size() << "has to be split";
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0)
+ ++ongoingSplitOperations;
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildReadOperation(ioRingHandle, readRequest->fd, span, offset,
+ quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::VectoredRead: {
+ auto *vectoredReadRequest = request.requestData<Operation::VectoredRead>();
+ auto span = vectoredReadRequest->destinations.front();
+ auto offset = vectoredReadRequest->offset;
+ if (Q_LIKELY(vectoredReadRequest->destinations.size() > 1
+ || span.size() > MaxReadWriteLen)) {
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0 && extra->spanIndex == 0)
+ ++ongoingSplitOperations;
+ extra->numSpans = vectoredReadRequest->destinations.size();
+
+ span = vectoredReadRequest->destinations[extra->spanIndex];
+
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildReadOperation(ioRingHandle, vectoredReadRequest->fd, span,
+ offset, quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::Write: {
+ auto *writeRequest = request.requestData<Operation::Write>();
+ auto span = writeRequest->source;
+ auto offset = writeRequest->offset;
+ if (span.size() > MaxReadWriteLen) {
+ qCDebug(lcQIORing) << "Requested Write of size" << span.size() << "has to be split";
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0)
+ ++ongoingSplitOperations;
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildWriteOperation(ioRingHandle, writeRequest->fd, span, offset,
+ quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::VectoredWrite: {
+ auto *vectoredWriteRequest = request.requestData<Operation::VectoredWrite>();
+ auto span = vectoredWriteRequest->sources.front();
+ auto offset = vectoredWriteRequest->offset;
+ if (Q_LIKELY(vectoredWriteRequest->sources.size() > 1
+ || span.size() > MaxReadWriteLen)) {
+ auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>();
+ if (extra->spanOffset == 0 && extra->spanIndex == 0)
+ ++ongoingSplitOperations;
+ extra->numSpans = vectoredWriteRequest->sources.size();
+
+ span = vectoredWriteRequest->sources[extra->spanIndex];
+
+ const qsizetype remaining = span.size() - extra->spanOffset;
+ span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen));
+ offset += extra->totalProcessed;
+ }
+ hr = buildWriteOperation(ioRingHandle, vectoredWriteRequest->fd, span,
+ offset, quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::Flush: {
+ if (ongoingSplitOperations > 0)
+ return RequestPrepResult::Defer;
+ auto *flushRequest = request.requestData<Operation::Flush>();
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef(HANDLE(flushRequest->fd));
+ hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_DEFAULT,
+ quintptr(std::addressof(request)),
+ IOSQE_FLAGS_DRAIN_PRECEDING_OPS);
+ break;
+ }
+ case QtPrivate::Operation::Stat: {
+ auto statRequest = request.takeRequestData<Operation::Stat>();
+ FILE_STANDARD_INFO info;
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ if (!GetFileInformationByHandleEx(HANDLE(statRequest.fd), FileStandardInfo, &info,
+ sizeof(info))) {
+ DWORD winErr = GetLastError();
+ QFileDevice::FileError error = QFileDevice::UnspecifiedError;
+ if (winErr == ERROR_FILE_NOT_FOUND || winErr == ERROR_INVALID_HANDLE)
+ error = QFileDevice::OpenError;
+ else if (winErr == ERROR_ACCESS_DENIED)
+ error = QFileDevice::PermissionsError;
+ statRequest.result.emplace<QFileDevice::FileError>(error);
+ } else {
+ auto &result = statRequest.result.emplace<QIORingResult<Operation::Stat>>();
+ result.size = info.EndOfFile.QuadPart;
+ }
+ invokeCallback(statRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ case Operation::Cancel: {
+ auto *cancelRequest = request.requestData<Operation::Cancel>();
+ auto *otherOperation = reinterpret_cast<GenericRequestType *>(cancelRequest->handle);
+ if (!otherOperation || !addrItMap.contains(otherOperation)) {
+ qCDebug(lcQIORing, "Invalid cancel for non-existant operation");
+ invokeCallback(*cancelRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ qCDebug(lcQIORing) << "Cancelling operation of type" << otherOperation->operation()
+ << "which was"
+ << (otherOperation->wasQueued() ? "queued" : "not queued");
+ Q_ASSERT(&request != otherOperation);
+ if (!otherOperation->wasQueued()) {
+ // The request hasn't been queued yet, so we can just drop it from
+ // the pending requests and call the callback.
+ auto it = addrItMap.take(otherOperation);
+ finishRequestWithError(*otherOperation, QFileDevice::AbortError);
+ pendingRequests.erase(it); // otherOperation is deleted
+ invokeCallback(*cancelRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ qintptr fd = -1;
+ invokeOnOp(*otherOperation, [&fd](auto *request) {
+ if constexpr (QtPrivate::OperationHasFd_v<decltype(*request)>)
+ fd = request->fd;
+ });
+ if (fd == -1) {
+ qCDebug(lcQIORing, "Invalid cancel for non-existant fd");
+ invokeCallback(*cancelRequest);
+ return RequestPrepResult::RequestCompleted;
+ }
+ // NOLINTNEXTLINE(performance-no-int-to-ptr)
+ const IORING_HANDLE_REF fileRef((HANDLE(fd)));
+ hr = BuildIoRingCancelRequest(ioRingHandle, fileRef, quintptr(otherOperation),
+ quintptr(std::addressof(request)));
+ break;
+ }
+ case Operation::NumOperations:
+ Q_UNREACHABLE_RETURN(RequestPrepResult::RequestCompleted);
+ break;
+ }
+ if (hr == IORING_E_SUBMISSION_QUEUE_FULL)
+ return RequestPrepResult::QueueFull;
+ if (FAILED(hr)) {
+ finishRequestWithError(request, QFileDevice::UnspecifiedError);
+ return RequestPrepResult::RequestCompleted;
+ }
+ request.setQueued(true);
+ return RequestPrepResult::Ok;
+}
+
+bool QIORing::verifyFd(GenericRequestType &req)
+{
+ bool result = true;
+ invokeOnOp(req, [&](auto *request) {
+ if constexpr (QtPrivate::OperationHasFd_v<decltype(*request)>) {
+ result = quintptr(request->fd) > 0 && quintptr(request->fd) != quintptr(INVALID_HANDLE_VALUE);
+ }
+ });
+ return result;
+}
+
+void QIORing::GenericRequestType::cleanupExtra(Operation op, void *extra)
+{
+ switch (op) {
+ case QtPrivate::Operation::Read:
+ case QtPrivate::Operation::VectoredRead:
+ case QtPrivate::Operation::Write:
+ case QtPrivate::Operation::VectoredWrite:
+ delete static_cast<QtPrivate::ReadWriteExtra *>(extra);
+ break;
+ case QtPrivate::Operation::Open:
+ case QtPrivate::Operation::Close:
+ case QtPrivate::Operation::Flush:
+ case QtPrivate::Operation::Stat:
+ case QtPrivate::Operation::Cancel:
+ case QtPrivate::Operation::NumOperations:
+ break;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/itemmodels/qrangemodel.cpp b/src/corelib/itemmodels/qrangemodel.cpp
index d72722f063d..f37812876ea 100644
--- a/src/corelib/itemmodels/qrangemodel.cpp
+++ b/src/corelib/itemmodels/qrangemodel.cpp
@@ -1385,6 +1385,7 @@ void QRangeModel::resetRoleNames()
/*!
\property QRangeModel::autoConnectPolicy
+ \since 6.11
\brief if and when the model auto-connects to property changed notifications.
If QRangeModel operates on a data structure that holds the same type of
diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h
index 3e35ad3981c..f6b08099fe7 100644
--- a/src/corelib/itemmodels/qrangemodel_impl.h
+++ b/src/corelib/itemmodels/qrangemodel_impl.h
@@ -23,6 +23,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qscopedvaluerollback.h>
#include <QtCore/qset.h>
+#include <QtCore/qvarlengtharray.h>
#include <algorithm>
#include <functional>
@@ -1203,76 +1204,48 @@ public:
return std::move(result.data());
}
+ static constexpr bool isRangeModelRole(int role)
+ {
+ return role == Qt::RangeModelDataRole
+ || role == Qt::RangeModelAdapterRole;
+ }
+
+ static constexpr bool isPrimaryRole(int role)
+ {
+ return role == Qt::DisplayRole || role == Qt::EditRole;
+ }
+
QMap<int, QVariant> itemData(const QModelIndex &index) const
{
QMap<int, QVariant> result;
- bool tried = false;
- const auto readItemData = [this, &index, &result, &tried](const auto &value){
- Q_UNUSED(this);
- Q_UNUSED(index);
- using value_type = q20::remove_cvref_t<decltype(value)>;
- using multi_role = QRangeModelDetails::is_multi_role<value_type>;
- using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
- if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
- using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
- tried = true;
- const auto roles = this->itemModel().roleNames().keys();
- for (auto &role : roles) {
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
- continue;
- QVariant data = ItemAccess::readRole(value, role);
- if (data.isValid())
- result[role] = std::move(data);
- }
- } else if constexpr (multi_role()) {
- tried = true;
- if constexpr (std::is_convertible_v<value_type, decltype(result)>) {
+ if (index.isValid()) {
+ bool tried = false;
+
+ // optimisation for items backed by a QMap<int, QVariant> or equivalent
+ readAt(index, [&result, &tried](const auto &value) {
+ if constexpr (std::is_convertible_v<decltype(value), decltype(result)>) {
+ tried = true;
result = value;
- } else {
- const auto roleNames = [this]() -> QHash<int, QByteArray> {
- Q_UNUSED(this);
- if constexpr (!multi_role::int_key)
- return this->itemModel().roleNames();
- else
- return {};
- }();
- for (auto it = std::begin(value); it != std::end(value); ++it) {
- const int role = [&roleNames, key = QRangeModelDetails::key(it)]() {
- Q_UNUSED(roleNames);
- if constexpr (multi_role::int_key)
- return int(key);
- else
- return roleNames.key(key.toUtf8(), -1);
- }();
-
- if (role != -1 && role != Qt::RangeModelDataRole && role != Qt::RangeModelAdapterRole)
- result.insert(role, QRangeModelDetails::value(it));
- }
}
- } else if constexpr (has_metaobject<value_type>) {
- if (row_traits::fixed_size() <= 1) {
- tried = true;
- const auto roleNames = this->itemModel().roleNames();
- const auto end = roleNames.keyEnd();
- for (auto it = roleNames.keyBegin(); it != end; ++it) {
- const int role = *it;
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
- continue;
- QVariant data = readRole(index, role, QRangeModelDetails::pointerTo(value));
- if (data.isValid())
- result[role] = std::move(data);
- }
+ });
+ if (!tried) {
+ const auto roles = this->itemModel().roleNames().keys();
+ QVarLengthArray<QModelRoleData, 16> roleDataArray;
+ roleDataArray.reserve(roles.size());
+ for (auto role : roles) {
+ if (isRangeModelRole(role))
+ continue;
+ roleDataArray.emplace_back(role);
}
- }
- };
-
- if (index.isValid()) {
- readAt(index, readItemData);
+ QModelRoleDataSpan roleDataSpan(roleDataArray);
+ multiData(index, roleDataSpan);
- if (!tried) { // no multi-role item found
- result = this->itemModel().QAbstractItemModel::itemData(index);
- result.remove(Qt::RangeModelAdapterRole);
+ for (auto &&roleData : std::move(roleDataSpan)) {
+ QVariant data = roleData.data();
+ if (data.isValid())
+ result[roleData.role()] = std::move(data);
+ }
}
}
return result;
@@ -1288,27 +1261,34 @@ public:
using multi_role = QRangeModelDetails::is_multi_role<value_type>;
using wrapped_value_type = QRangeModelDetails::wrapped_t<value_type>;
+ const auto readModelData = [&value](QModelRoleData &roleData){
+ const int role = roleData.role();
+ if (role == Qt::RangeModelDataRole) {
+ // Qt QML support: "modelData" role returns the entire multi-role item.
+ // QML can only use raw pointers to QObject (so we unwrap), and gadgets
+ // only by value (so we take the reference).
+ if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
+ roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
+ else
+ roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
+ } else if (role == Qt::RangeModelAdapterRole) {
+ // for QRangeModelAdapter however, we want to respect smart pointer wrappers
+ if constexpr (std::is_copy_assignable_v<value_type>)
+ roleData.setData(QVariant::fromValue(value));
+ else
+ roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
+ } else {
+ return false;
+ }
+ return true;
+ };
+
if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
tried = true;
for (auto &roleData : roleDataSpan) {
- if (roleData.role() == Qt::RangeModelDataRole) {
- // Qt QML support: "modelData" role returns the entire multi-role item.
- // QML can only use raw pointers to QObject (so we unwrap), and gadgets
- // only by value (so we take the reference).
- if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
- roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else if (roleData.role() == Qt::RangeModelAdapterRole) {
- // for QRangeModelAdapter however, we want to respect smart pointer wrappers
- if constexpr (std::is_copy_assignable_v<value_type>)
- roleData.setData(QVariant::fromValue(value));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else {
+ if (!readModelData(roleData))
roleData.setData(ItemAccess::readRole(value, roleData.role()));
- }
}
} else if constexpr (multi_role()) {
tried = true;
@@ -1337,21 +1317,7 @@ public:
if (row_traits::fixed_size() <= 1) {
tried = true;
for (auto &roleData : roleDataSpan) {
- if (roleData.role() == Qt::RangeModelDataRole) {
- // Qt QML support: "modelData" role returns the entire multi-role item.
- // QML can only use raw pointers to QObject (so we unwrap), and gadgets
- // only by value (so we take the reference).
- if constexpr (std::is_copy_assignable_v<wrapped_value_type>)
- roleData.setData(QVariant::fromValue(QRangeModelDetails::refTo(value)));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else if (roleData.role() == Qt::RangeModelAdapterRole) {
- // for QRangeModelAdapter however, we want to respect smart pointer wrappers
- if constexpr (std::is_copy_assignable_v<value_type>)
- roleData.setData(QVariant::fromValue(value));
- else
- roleData.setData(QVariant::fromValue(QRangeModelDetails::pointerTo(value)));
- } else {
+ if (!readModelData(roleData)) {
roleData.setData(readRole(index, roleData.role(),
QRangeModelDetails::pointerTo(value)));
}
@@ -1360,7 +1326,7 @@ public:
tried = true;
for (auto &roleData : roleDataSpan) {
const int role = roleData.role();
- if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ if (isPrimaryRole(role)) {
roleData.setData(readProperty(index.column(),
QRangeModelDetails::pointerTo(value)));
} else {
@@ -1372,12 +1338,10 @@ public:
tried = true;
for (auto &roleData : roleDataSpan) {
const int role = roleData.role();
- if (role == Qt::DisplayRole || role == Qt::EditRole
- || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) {
+ if (isPrimaryRole(role) || isRangeModelRole(role))
roleData.setData(read(value));
- } else {
+ else
roleData.clearData();
- }
}
}
});
@@ -1395,7 +1359,8 @@ public:
auto emitDataChanged = qScopeGuard([&success, this, &index, role]{
if (success) {
Q_EMIT this->dataChanged(index, index,
- role == Qt::EditRole || role == Qt::RangeModelDataRole || role == Qt::RangeModelDataRole
+ role == Qt::EditRole || role == Qt::RangeModelDataRole
+ || role == Qt::RangeModelAdapterRole
? QList<int>{} : QList<int>{role});
}
});
@@ -1408,25 +1373,34 @@ public:
using multi_role = QRangeModelDetails::is_multi_role<value_type>;
auto setRangeModelDataRole = [&target, &data]{
- auto &targetRef = QRangeModelDetails::refTo(target);
constexpr auto targetMetaType = QMetaType::fromType<value_type>();
const auto dataMetaType = data.metaType();
constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
// we don't support replacing objects that are stored as raw pointers,
// as this makes object ownership very messy. But we can replace objects
- // stored in smart pointers.
- if constexpr (isWrapped && !std::is_pointer_v<value_type>
- && std::is_copy_assignable_v<value_type>) {
- if (data.canConvert(targetMetaType)) {
- target = data.value<value_type>();
- return true;
+ // stored in smart pointers, and we can initialize raw nullptr objects.
+ if constexpr (isWrapped) {
+ constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
+ if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
+ if (data.canConvert(targetMetaType)) {
+ target = data.value<value_type>();
+ return true;
+ }
+ } else if constexpr (is_raw_pointer) {
+ if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
+ target = data.value<value_type>();
+ return true;
+ }
+ } else {
+ Q_UNUSED(target);
}
}
// Otherwise we have a move-only or polymorph type. fall through to
// error handling.
} else if constexpr (isWrapped) {
if (QRangeModelDetails::isValid(target)) {
+ auto &targetRef = QRangeModelDetails::refTo(target);
// we need to get a wrapped value type out of the QVariant, which
// might carry a pointer. We have to try all alternatives.
if (const auto mt = QMetaType::fromType<wrapped_value_type>();
@@ -1440,10 +1414,10 @@ public:
}
}
} else if (targetMetaType == dataMetaType) {
- targetRef = data.value<value_type>();
+ QRangeModelDetails::refTo(target) = data.value<value_type>();
return true;
} else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
- targetRef = *data.value<value_type *>();
+ QRangeModelDetails::refTo(target) = *data.value<value_type *>();
return true;
}
#ifndef QT_NO_DEBUG
@@ -1455,17 +1429,16 @@ public:
if constexpr (QRangeModelDetails::item_access<wrapped_value_type>()) {
using ItemAccess = QRangeModelDetails::QRangeModelItemAccess<wrapped_value_type>;
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
+ if (isRangeModelRole(role))
return setRangeModelDataRole();
return ItemAccess::writeRole(target, data, role);
} if constexpr (has_metaobject<value_type>) {
if (row_traits::fixed_size() <= 1) { // multi-role value
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
+ if (isRangeModelRole(role))
return setRangeModelDataRole();
return writeRole(role, QRangeModelDetails::pointerTo(target), data);
} else if (column <= row_traits::fixed_size() // multi-column
- && (role == Qt::DisplayRole || role == Qt::EditRole
- || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)) {
+ && (isPrimaryRole(role) || isRangeModelRole(role))) {
return writeProperty(column, QRangeModelDetails::pointerTo(target), data);
}
} else if constexpr (multi_role::value) {
@@ -1492,14 +1465,20 @@ public:
return write(target[roleToSet], data);
else
return write(target[roleNames.value(roleToSet)], data);
- } else if (role == Qt::DisplayRole || role == Qt::EditRole
- || role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole) {
+ } else if (isPrimaryRole(role) || isRangeModelRole(role)) {
return write(target, data);
}
return false;
};
success = writeAt(index, writeData);
+
+ if constexpr (itemsAreQObjects) {
+ if (success && isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
+ if (QObject *item = data.value<QObject *>())
+ Self::connectProperties(index, item, m_data.context, m_data.properties);
+ }
+ }
}
return success;
}
@@ -1607,7 +1586,7 @@ public:
tried = true;
auto targetCopy = makeCopy(target);
for (auto &&[role, value] : data.asKeyValueRange()) {
- if (role == Qt::RangeModelDataRole || role == Qt::RangeModelAdapterRole)
+ if (isRangeModelRole(role))
continue;
if (!writeRole(role, QRangeModelDetails::pointerTo(targetCopy), value)) {
const QByteArray roleName = roleNames.value(role);
diff --git a/src/corelib/itemmodels/qrangemodeladapter.qdoc b/src/corelib/itemmodels/qrangemodeladapter.qdoc
index 5ab128a8c5f..263bff0dd0c 100644
--- a/src/corelib/itemmodels/qrangemodeladapter.qdoc
+++ b/src/corelib/itemmodels/qrangemodeladapter.qdoc
@@ -118,11 +118,9 @@
would bypass the QAbstractItemModel notification protocol, those reference
objects prevent direct modifications of the items.
- \note Calling mutable overloads generates some overhead. Make a const copy
- of the adapter (which will not copy the data), or use \c{std::as_const} to
- make sure that only the const overloads are used in performance critical,
- read-heavy code. This is the same technique as when accessing elements in
- an implicitly shared Qt container.
+ \note Accessing the reference object always makes a call to the model to get
+ a copy of the value. This can be expensive; for performance critical access
+ to data, store a copy.
\section3 Item access
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index a5d34eac707..c7e50788b45 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -469,6 +469,33 @@ QMetaType QMetaObject::metaType() const
}
}
+static inline QByteArrayView objectMetaObjectHash(const QMetaObject *m)
+{
+ // metaObjectHash didn't exist before revision 14
+ if (QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && priv(m->d.data)->revision < 14)
+ return {};
+ const auto index = priv(m->d.data)->metaObjectHashIndex;
+ if (index == -1)
+ return {};
+ return stringDataView(m, index);
+}
+
+/*!
+ \since 6.11
+
+ Returns the revisioned hash of the contents of this QMetaObject or nullptr.
+
+ The hash has the following format <hash_revision>$<hash_b64>, where
+ hash_revision is an integer and hash_b64 is the base64 encoding of the
+ hash.
+
+ Note that only hashes of the same revision should be compared.
+*/
+const char *QMetaObject::metaObjectHash() const
+{
+ return objectMetaObjectHash(this).constData();
+}
+
/*!
Returns the method offset for this class; i.e. the index position
of this class's first member function.
@@ -4405,6 +4432,34 @@ bool QMetaProperty::isFinal() const
}
/*!
+ \since 6.11
+ Returns \c true if the property is virtual; otherwise returns \c false.
+
+ A property is virtual if the \c{Q_PROPERTY()}'s \c VIRTUAL attribute
+ is set.
+*/
+bool QMetaProperty::isVirtual() const
+{
+ if (!mobj)
+ return false;
+ return data.flags() & Virtual;
+}
+
+/*!
+ \since 6.11
+ Returns \c true if the property does override; otherwise returns \c false.
+
+ A property does override if the \c{Q_PROPERTY()}'s \c OVERRIDE attribute
+ is set.
+*/
+bool QMetaProperty::isOverride() const
+{
+ if (!mobj)
+ return false;
+ return data.flags() & Override;
+}
+
+/*!
\since 5.15
Returns \c true if the property is required; otherwise returns \c false.
diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h
index 0f793ca753b..ff3cc751c3a 100644
--- a/src/corelib/kernel/qmetaobject.h
+++ b/src/corelib/kernel/qmetaobject.h
@@ -365,6 +365,8 @@ public:
bool isUser() const;
bool isConstant() const;
bool isFinal() const;
+ bool isVirtual() const;
+ bool isOverride() const;
bool isRequired() const;
bool isBindable() const;
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index bfda30fda28..7264d2a956f 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -124,6 +124,7 @@ struct QMetaObjectPrivate
int constructorCount, constructorData;
int flags;
int signalCount;
+ int metaObjectHashIndex;
static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject)
{ return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); }
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index 6065bf2baea..9af6de73680 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -558,6 +558,8 @@ QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty &protot
property.setEnumOrFlag(prototype.isEnumType());
property.setConstant(prototype.isConstant());
property.setFinal(prototype.isFinal());
+ property.setVirtual(prototype.isVirtual());
+ property.setOverride(prototype.isOverride());
property.setRevision(prototype.revision());
if (prototype.hasNotifySignal()) {
// Find an existing method for the notify signal, or add a new one.
@@ -1177,10 +1179,11 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
int methodParametersDataSize = aggregateParameterCount(d->methods)
+ aggregateParameterCount(d->constructors);
if constexpr (mode == Construct) {
- static_assert(QMetaObjectPrivate::OutputRevision == 13, "QMetaObjectBuilder should generate the same version as moc");
+ static_assert(QMetaObjectPrivate::OutputRevision == 14, "QMetaObjectBuilder should generate the same version as moc");
pmeta->revision = QMetaObjectPrivate::OutputRevision;
pmeta->flags = d->flags.toInt() | AllocatedMetaObject;
pmeta->className = 0; // Class name is always the first string.
+ pmeta->metaObjectHashIndex = -1; // TODO support hash in the builder too
//pmeta->signalCount is handled in the "output method loop" as an optimization.
pmeta->classInfoCount = d->classInfoNames.size();
@@ -2068,6 +2071,32 @@ bool QMetaPropertyBuilder::isFinal() const
}
/*!
+ Returns \c true if the property is virtual; otherwise returns \c false.
+ The default value is false.
+*/
+bool QMetaPropertyBuilder::isVirtual() const
+{
+ QMetaPropertyBuilderPrivate *d = d_func();
+ if (d)
+ return d->flag(Virtual);
+ else
+ return false;
+}
+
+/*!
+ Returns \c true if the property does override; otherwise returns \c false.
+ The default value is false.
+*/
+bool QMetaPropertyBuilder::isOverride() const
+{
+ QMetaPropertyBuilderPrivate *d = d_func();
+ if (d)
+ return d->flag(Override);
+ else
+ return false;
+}
+
+/*!
* Returns \c true if the property is an alias.
* The default value is false
*/
@@ -2239,6 +2268,30 @@ void QMetaPropertyBuilder::setFinal(bool value)
}
/*!
+ Sets the \c VIRTUAL flag on this property to \a value.
+
+ \sa isFinal()
+*/
+void QMetaPropertyBuilder::setVirtual(bool value)
+{
+ QMetaPropertyBuilderPrivate *d = d_func();
+ if (d)
+ d->setFlag(Virtual, value);
+}
+
+/*!
+ Sets the \c OVERRIDE flag on this property to \a value.
+
+ \sa isOverride()
+*/
+void QMetaPropertyBuilder::setOverride(bool value)
+{
+ QMetaPropertyBuilderPrivate *d = d_func();
+ if (d)
+ d->setFlag(Override, value);
+}
+
+/*!
Sets the \c ALIAS flag on this property to \a value
*/
void QMetaPropertyBuilder::setAlias(bool value)
diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h
index 563704d60e6..9591944602a 100644
--- a/src/corelib/kernel/qmetaobjectbuilder_p.h
+++ b/src/corelib/kernel/qmetaobjectbuilder_p.h
@@ -214,6 +214,8 @@ public:
bool isEnumOrFlag() const;
bool isConstant() const;
bool isFinal() const;
+ bool isVirtual() const;
+ bool isOverride() const;
bool isAlias() const;
bool isBindable() const;
bool isRequired() const;
@@ -229,6 +231,8 @@ public:
void setEnumOrFlag(bool value);
void setConstant(bool value);
void setFinal(bool value);
+ void setVirtual(bool value);
+ void setOverride(bool value);
void setAlias(bool value);
void setBindable(bool value);
void setRequired(bool value);
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 848102cc57a..d3e761982f5 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -247,6 +247,8 @@ struct Q_CORE_EXPORT QMetaObject
QMetaType metaType() const;
+ const char *metaObjectHash() const;
+
int methodOffset() const;
int enumeratorOffset() const;
int propertyOffset() const;
diff --git a/src/corelib/kernel/qtmocconstants.h b/src/corelib/kernel/qtmocconstants.h
index 79c0138bb28..822e02e6c8e 100644
--- a/src/corelib/kernel/qtmocconstants.h
+++ b/src/corelib/kernel/qtmocconstants.h
@@ -30,7 +30,8 @@ namespace QtMocConstants {
// revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array
// revision 12 is Qt 6.6: It adds the metatype for enums
// revision 13 is Qt 6.9: Adds support for 64-bit QFlags and moves the method revision
-enum { OutputRevision = 13 }; // Used by moc, qmetaobjectbuilder and qdbus
+// revision 14 is Qt 6.11: Adds a hash of meta object contents
+enum { OutputRevision = 14 }; // Used by moc, qmetaobjectbuilder and qdbus
enum PropertyFlags : uint {
Invalid = 0x00000000,
@@ -39,7 +40,8 @@ enum PropertyFlags : uint {
Resettable = 0x00000004,
EnumOrFlag = 0x00000008,
Alias = 0x00000010,
- // Reserved for future usage = 0x00000020,
+ Virtual = 0x00000020,
+ Override = 0x00000040,
StdCppSet = 0x00000100,
Constant = 0x00000400,
Final = 0x00000800,
diff --git a/src/corelib/kernel/qtmochelpers.h b/src/corelib/kernel/qtmochelpers.h
index 4c549e78ad5..3d2b59d2a73 100644
--- a/src/corelib/kernel/qtmochelpers.h
+++ b/src/corelib/kernel/qtmochelpers.h
@@ -511,7 +511,8 @@ template <typename ObjectType, typename Unique, typename Strings,
typename Constructors = UintData<>, typename ClassInfo = detail::UintDataBlock<0, 0>>
constexpr auto metaObjectData(uint flags, const Strings &strings,
const Methods &methods, const Properties &properties,
- const Enums &enums, const Constructors &constructors = {},
+ const Enums &enums, int qt_metaObjectHashIndex = -1,
+ const Constructors &constructors = {},
const ClassInfo &classInfo = {})
{
constexpr uint MetaTypeCount = Properties::metaTypeCount()
@@ -520,7 +521,7 @@ constexpr auto metaObjectData(uint flags, const Strings &strings,
+ Methods::metaTypeCount()
+ Constructors::metaTypeCount();
- constexpr uint HeaderSize = 14;
+ constexpr uint HeaderSize = 15;
constexpr uint TotalSize = HeaderSize
+ Properties::dataSize()
+ Enums::dataSize()
@@ -582,6 +583,8 @@ constexpr auto metaObjectData(uint flags, const Strings &strings,
}
}
+ data[14] = qt_metaObjectHashIndex;
+
return result;
}
diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp
index de7043e8c1d..9c26de94b6d 100644
--- a/src/corelib/mimetypes/qmimeprovider.cpp
+++ b/src/corelib/mimetypes/qmimeprovider.cpp
@@ -512,8 +512,8 @@ QMimeBinaryProvider::MimeTypeExtraMap::const_iterator
QMimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName)
{
#if QT_CONFIG(xmlstreamreader)
- auto it = m_mimetypeExtra.find(mimeName);
- if (it == m_mimetypeExtra.cend()) {
+ auto [it, insertionOccurred] = m_mimetypeExtra.try_emplace(mimeName);
+ if (insertionOccurred) {
// load comment and globPatterns
// shared-mime-info since 1.3 lowercases the xml files
@@ -523,9 +523,8 @@ QMimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName)
QFile qfile(mimeFile);
if (!qfile.open(QFile::ReadOnly))
- return m_mimetypeExtra.cend();
+ return it;
- it = m_mimetypeExtra.try_emplace(mimeName).first;
MimeTypeExtra &extra = it->second;
QString mainPattern;
diff --git a/src/corelib/tools/qflatmap_p.h b/src/corelib/tools/qflatmap_p.h
index 5a827fb4148..bdb0e24dde8 100644
--- a/src/corelib/tools/qflatmap_p.h
+++ b/src/corelib/tools/qflatmap_p.h
@@ -609,14 +609,18 @@ public:
T value(const Key &key) const
{
auto it = find(key);
- return it == end() ? T() : it.value();
+ if (it == end())
+ return T();
+ return it.value();
}
template <class X, class Y = Compare, is_marked_transparent<Y> = nullptr>
T value(const X &key) const
{
auto it = find(key);
- return it == end() ? T() : it.value();
+ if (it == end())
+ return T();
+ return it.value();
}
T &operator[](const Key &key)
@@ -899,12 +903,13 @@ private:
T do_take(iterator it)
{
- if (it != end()) {
+ if (it == end())
+ return {};
+ return [&] {
T result = std::move(it.value());
erase(it);
return result;
- }
- return {};
+ }();
}
template <class InputIt, is_compatible_iterator<InputIt> = nullptr>
diff --git a/src/corelib/tools/qmargins.h b/src/corelib/tools/qmargins.h
index f833a338b16..cbdb093adc8 100644
--- a/src/corelib/tools/qmargins.h
+++ b/src/corelib/tools/qmargins.h
@@ -333,20 +333,13 @@ private:
qreal m_right;
qreal m_bottom;
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_FLOAT_COMPARE
friend constexpr bool qFuzzyCompare(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
{
- return ((!lhs.m_left || !rhs.m_left) ? qFuzzyIsNull(lhs.m_left - rhs.m_left)
- : qFuzzyCompare(lhs.m_left, rhs.m_left))
- && ((!lhs.m_top || !rhs.m_top) ? qFuzzyIsNull(lhs.m_top - rhs.m_top)
- : qFuzzyCompare(lhs.m_top, rhs.m_top))
- && ((!lhs.m_right || !rhs.m_right) ? qFuzzyIsNull(lhs.m_right - rhs.m_right)
- : qFuzzyCompare(lhs.m_right, rhs.m_right))
- && ((!lhs.m_bottom || !rhs.m_bottom) ? qFuzzyIsNull(lhs.m_bottom - rhs.m_bottom)
- : qFuzzyCompare(lhs.m_bottom, rhs.m_bottom));
+ return QtPrivate::fuzzyCompare(lhs.m_left, rhs.m_left)
+ && QtPrivate::fuzzyCompare(lhs.m_top, rhs.m_top)
+ && QtPrivate::fuzzyCompare(lhs.m_right, rhs.m_right)
+ && QtPrivate::fuzzyCompare(lhs.m_bottom, rhs.m_bottom);
}
- QT_WARNING_POP
friend constexpr bool qFuzzyIsNull(const QMarginsF &m) noexcept
{
return qFuzzyIsNull(m.m_left) && qFuzzyIsNull(m.m_top)
diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h
index ae896ba7079..1b767324058 100644
--- a/src/corelib/tools/qpoint.h
+++ b/src/corelib/tools/qpoint.h
@@ -259,14 +259,11 @@ public:
}
private:
- QT_WARNING_PUSH
- QT_WARNING_DISABLE_FLOAT_COMPARE
friend constexpr bool qFuzzyCompare(const QPointF &p1, const QPointF &p2) noexcept
{
- return ((!p1.xp || !p2.xp) ? qFuzzyIsNull(p1.xp - p2.xp) : qFuzzyCompare(p1.xp, p2.xp))
- && ((!p1.yp || !p2.yp) ? qFuzzyIsNull(p1.yp - p2.yp) : qFuzzyCompare(p1.yp, p2.yp));
+ return QtPrivate::fuzzyCompare(p1.xp, p2.xp)
+ && QtPrivate::fuzzyCompare(p1.yp, p2.yp);
}
- QT_WARNING_POP
friend constexpr bool qFuzzyIsNull(const QPointF &point) noexcept
{
return qFuzzyIsNull(point.xp) && qFuzzyIsNull(point.yp);
diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h
index 86509cb6483..1c5b02ed1f0 100644
--- a/src/corelib/tools/qsize.h
+++ b/src/corelib/tools/qsize.h
@@ -258,10 +258,9 @@ private:
QT_WARNING_DISABLE_FLOAT_COMPARE
friend constexpr bool qFuzzyCompare(const QSizeF &s1, const QSizeF &s2) noexcept
{
- // Cannot use qFuzzyCompare(), because it will give incorrect results
// if one of the arguments is 0.0.
- return ((!s1.wd || !s2.wd) ? qFuzzyIsNull(s1.wd - s2.wd) : qFuzzyCompare(s1.wd, s2.wd))
- && ((!s1.ht || !s2.ht) ? qFuzzyIsNull(s1.ht - s2.ht) : qFuzzyCompare(s1.ht, s2.ht));
+ return QtPrivate::fuzzyCompare(s1.wd, s2.wd)
+ && QtPrivate::fuzzyCompare(s1.ht, s2.ht);
}
QT_WARNING_POP
friend constexpr bool qFuzzyIsNull(const QSizeF &size) noexcept