summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools')
-rw-r--r--src/corelib/tools/qarraydata.cpp128
-rw-r--r--src/corelib/tools/qarraydata.h72
-rw-r--r--src/corelib/tools/qarraydataops.h21
-rw-r--r--src/corelib/tools/qarraydatapointer.h48
-rw-r--r--src/corelib/tools/qatomicscopedvaluerollback.h (renamed from src/corelib/tools/qatomicscopedvaluerollback_p.h)70
-rw-r--r--src/corelib/tools/qatomicscopedvaluerollback.qdoc123
-rw-r--r--src/corelib/tools/qbitarray.cpp356
-rw-r--r--src/corelib/tools/qbitarray.h135
-rw-r--r--src/corelib/tools/qcommandlineoption.cpp6
-rw-r--r--src/corelib/tools/qcommandlineparser.cpp35
-rw-r--r--src/corelib/tools/qcontainerfwd.h16
-rw-r--r--src/corelib/tools/qcontainertools_impl.h18
-rw-r--r--src/corelib/tools/qcontiguouscache.h1
-rw-r--r--src/corelib/tools/qcryptographichash.cpp216
-rw-r--r--src/corelib/tools/qcryptographichash.h14
-rw-r--r--src/corelib/tools/qduplicatetracker_p.h2
-rw-r--r--src/corelib/tools/qeasingcurve.cpp33
-rw-r--r--src/corelib/tools/qeasingcurve.h8
-rw-r--r--src/corelib/tools/qflatmap_p.h4
-rw-r--r--src/corelib/tools/qfreelist.cpp10
-rw-r--r--src/corelib/tools/qfunctionaltools_impl.cpp47
-rw-r--r--src/corelib/tools/qfunctionaltools_impl.h1
-rw-r--r--src/corelib/tools/qhash.cpp534
-rw-r--r--src/corelib/tools/qhash.h409
-rw-r--r--src/corelib/tools/qhashfunctions.h85
-rw-r--r--src/corelib/tools/qiterator.h21
-rw-r--r--src/corelib/tools/qiterator.qdoc5
-rw-r--r--src/corelib/tools/qline.cpp41
-rw-r--r--src/corelib/tools/qline.h42
-rw-r--r--src/corelib/tools/qlist.h78
-rw-r--r--src/corelib/tools/qlist.qdoc58
-rw-r--r--src/corelib/tools/qmap.h36
-rw-r--r--src/corelib/tools/qmap.qdoc23
-rw-r--r--src/corelib/tools/qmargins.cpp32
-rw-r--r--src/corelib/tools/qmargins.h49
-rw-r--r--src/corelib/tools/qmessageauthenticationcode.h24
-rw-r--r--src/corelib/tools/qminimalflatset_p.h156
-rw-r--r--src/corelib/tools/qmultimap.qdoc12
-rw-r--r--src/corelib/tools/qpair.h6
-rw-r--r--src/corelib/tools/qpoint.cpp47
-rw-r--r--src/corelib/tools/qpoint.h26
-rw-r--r--src/corelib/tools/qrect.cpp44
-rw-r--r--src/corelib/tools/qrect.h28
-rw-r--r--src/corelib/tools/qringbuffer.cpp9
-rw-r--r--src/corelib/tools/qscopedpointer.cpp2
-rw-r--r--src/corelib/tools/qscopedpointer.h7
-rw-r--r--src/corelib/tools/qscopedvaluerollback.h4
-rw-r--r--src/corelib/tools/qscopeguard.h5
-rw-r--r--src/corelib/tools/qset.h32
-rw-r--r--src/corelib/tools/qset.qdoc30
-rw-r--r--src/corelib/tools/qshareddata.h11
-rw-r--r--src/corelib/tools/qsharedpointer.cpp93
-rw-r--r--src/corelib/tools/qsharedpointer.h26
-rw-r--r--src/corelib/tools/qsharedpointer_impl.h93
-rw-r--r--src/corelib/tools/qsize.cpp33
-rw-r--r--src/corelib/tools/qsize.h23
-rw-r--r--src/corelib/tools/qspan.h475
-rw-r--r--src/corelib/tools/qspan.qdoc683
-rw-r--r--src/corelib/tools/qspan_p.h24
-rw-r--r--src/corelib/tools/qtaggedpointer.h6
-rw-r--r--src/corelib/tools/qtimeline.cpp42
-rw-r--r--src/corelib/tools/qtools_p.h3
-rw-r--r--src/corelib/tools/qtyperevision.cpp217
-rw-r--r--src/corelib/tools/qtyperevision.h167
-rw-r--r--src/corelib/tools/quniquehandle_p.h225
-rw-r--r--src/corelib/tools/qvarlengtharray.h19
-rw-r--r--src/corelib/tools/qvarlengtharray.qdoc32
-rw-r--r--src/corelib/tools/qversionnumber.cpp261
-rw-r--r--src/corelib/tools/qversionnumber.h275
69 files changed, 4725 insertions, 1192 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp
index b1634d61b5..6aebd4306a 100644
--- a/src/corelib/tools/qarraydata.cpp
+++ b/src/corelib/tools/qarraydata.cpp
@@ -54,8 +54,8 @@ qsizetype qCalculateBlockSize(qsizetype elementCount, qsizetype elementSize, qsi
Q_ASSERT(elementSize);
size_t bytes;
- if (Q_UNLIKELY(mul_overflow(size_t(elementSize), size_t(elementCount), &bytes)) ||
- Q_UNLIKELY(add_overflow(bytes, size_t(headerSize), &bytes)))
+ if (Q_UNLIKELY(qMulOverflow(size_t(elementSize), size_t(elementCount), &bytes)) ||
+ Q_UNLIKELY(qAddOverflow(bytes, size_t(headerSize), &bytes)))
return -1;
if (Q_UNLIKELY(qsizetype(bytes) < 0))
return -1;
@@ -107,33 +107,30 @@ qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizet
return result;
}
-/*!
- \internal
+/*
+ Calculate the byte size for a block of \a capacity objects of size \a
+ objectSize, with a header of size \a headerSize. If the \a option is
+ QArrayData::Grow, the capacity itself adjusted up, preallocating room for
+ more elements to be added later; otherwise, it is an exact calculation.
- Returns \a allocSize plus extra reserved bytes necessary to store '\0'.
- */
-static inline qsizetype reserveExtraBytes(qsizetype allocSize)
+ Returns a structure containing the size in bytes and elements available.
+*/
+static inline CalculateGrowingBlockSizeResult
+calculateBlockSize(qsizetype capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option)
{
- // We deal with QByteArray and QString only
- constexpr qsizetype extra = qMax(sizeof(QByteArray::value_type), sizeof(QString::value_type));
- if (Q_UNLIKELY(allocSize < 0))
- return -1;
- if (Q_UNLIKELY(add_overflow(allocSize, extra, &allocSize)))
- return -1;
- return allocSize;
-}
+ // Adjust the header size up to account for the trailing null for QString
+ // and QByteArray. This is not checked for overflow because headers sizes
+ // should not be anywhere near the overflow limit.
+ constexpr qsizetype FooterSize = qMax(sizeof(QString::value_type), sizeof(QByteArray::value_type));
+ if (objectSize <= FooterSize)
+ headerSize += FooterSize;
-static inline qsizetype calculateBlockSize(qsizetype &capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option)
-{
- // Calculate the byte size
// allocSize = objectSize * capacity + headerSize, but checked for overflow
// plus padded to grow in size
if (option == QArrayData::Grow) {
- auto r = qCalculateGrowingBlockSize(capacity, objectSize, headerSize);
- capacity = r.elementCount;
- return r.size;
+ return qCalculateGrowingBlockSize(capacity, objectSize, headerSize);
} else {
- return qCalculateBlockSize(capacity, objectSize, headerSize);
+ return { qCalculateBlockSize(capacity, objectSize, headerSize), capacity };
}
}
@@ -148,27 +145,20 @@ static QArrayData *allocateData(qsizetype allocSize)
return header;
}
-
namespace {
-// QArrayData with strictest alignment requirements supported by malloc()
-struct alignas(std::max_align_t) AlignedQArrayData : QArrayData
-{
+struct AllocationResult {
+ void *data;
+ QArrayData *header;
};
}
+using QtPrivate::AlignedQArrayData;
-
-void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype alignment,
- qsizetype capacity, QArrayData::AllocationOption option) noexcept
+static inline AllocationResult
+allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity,
+ QArrayData::AllocationOption option) noexcept
{
- Q_ASSERT(dptr);
- // Alignment is a power of two
- Q_ASSERT(alignment >= qsizetype(alignof(QArrayData))
- && !(alignment & (alignment - 1)));
-
- if (capacity == 0) {
- *dptr = nullptr;
- return nullptr;
- }
+ if (capacity == 0)
+ return {};
qsizetype headerSize = sizeof(AlignedQArrayData);
const qsizetype headerAlignment = alignof(AlignedQArrayData);
@@ -177,16 +167,16 @@ void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype al
// Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding
// bytes so we can properly align the data array. This assumes malloc is
// able to provide appropriate alignment for the header -- as it should!
+ // Effectively, we allocate one QTypedArrayData<T>::AlignmentDummy.
headerSize += alignment - headerAlignment;
}
Q_ASSERT(headerSize > 0);
- qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option);
- allocSize = reserveExtraBytes(allocSize);
- if (Q_UNLIKELY(allocSize < 0)) { // handle overflow. cannot allocate reliably
- *dptr = nullptr;
- return nullptr;
- }
+ auto blockSize = calculateBlockSize(capacity, objectSize, headerSize, option);
+ capacity = blockSize.elementCount;
+ qsizetype allocSize = blockSize.size;
+ if (Q_UNLIKELY(allocSize < 0)) // handle overflow. cannot allocate reliably
+ return {};
QArrayData *header = allocateData(allocSize);
void *data = nullptr;
@@ -196,20 +186,54 @@ void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype al
header->alloc = qsizetype(capacity);
}
- *dptr = header;
- return data;
+ return { data, header };
}
-QPair<QArrayData *, void *>
+// Generic size and alignment allocation function
+void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype alignment,
+ qsizetype capacity, AllocationOption option) noexcept
+{
+ Q_ASSERT(dptr);
+ // Alignment is a power of two
+ Q_ASSERT(alignment >= qsizetype(alignof(QArrayData))
+ && !(alignment & (alignment - 1)));
+
+ auto r = allocateHelper(objectSize, alignment, capacity, option);
+ *dptr = r.header;
+ return r.data;
+}
+
+// Fixed size and alignment allocation functions
+void *QArrayData::allocate1(QArrayData **dptr, qsizetype capacity, AllocationOption option) noexcept
+{
+ Q_ASSERT(dptr);
+
+ auto r = allocateHelper(1, alignof(AlignedQArrayData), capacity, option);
+ *dptr = r.header;
+ return r.data;
+}
+
+void *QArrayData::allocate2(QArrayData **dptr, qsizetype capacity, AllocationOption option) noexcept
+{
+ Q_ASSERT(dptr);
+
+ auto r = allocateHelper(2, alignof(AlignedQArrayData), capacity, option);
+ *dptr = r.header;
+ return r.data;
+}
+
+std::pair<QArrayData *, void *>
QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer,
qsizetype objectSize, qsizetype capacity, AllocationOption option) noexcept
{
Q_ASSERT(!data || !data->isShared());
const qsizetype headerSize = sizeof(AlignedQArrayData);
- qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option);
+ auto r = calculateBlockSize(capacity, objectSize, headerSize, option);
+ qsizetype allocSize = r.size;
+ capacity = r.elementCount;
if (Q_UNLIKELY(allocSize < 0))
- return qMakePair<QArrayData *, void *>(nullptr, nullptr);
+ return {};
const qptrdiff offset = dataPointer
? reinterpret_cast<char *>(dataPointer) - reinterpret_cast<char *>(data)
@@ -217,10 +241,6 @@ QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer,
Q_ASSERT(offset > 0);
Q_ASSERT(offset <= allocSize); // equals when all free space is at the beginning
- allocSize = reserveExtraBytes(allocSize);
- if (Q_UNLIKELY(allocSize < 0)) // handle overflow. cannot reallocate reliably
- return qMakePair(data, dataPointer);
-
QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize)));
if (header) {
header->alloc = capacity;
@@ -228,7 +248,7 @@ QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer,
} else {
dataPointer = nullptr;
}
- return qMakePair(static_cast<QArrayData *>(header), dataPointer);
+ return {header, dataPointer};
}
void QArrayData::deallocate(QArrayData *data, qsizetype objectSize,
diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h
index 766596fe18..da83fc1a21 100644
--- a/src/corelib/tools/qarraydata.h
+++ b/src/corelib/tools/qarraydata.h
@@ -7,10 +7,18 @@
#include <QtCore/qpair.h>
#include <QtCore/qatomic.h>
+#include <QtCore/qflags.h>
+#include <QtCore/qcontainerfwd.h>
#include <string.h>
QT_BEGIN_NAMESPACE
+#if __has_cpp_attribute(gnu::malloc)
+# define Q_DECL_MALLOCLIKE [[nodiscard, gnu::malloc]]
+#else
+# define Q_DECL_MALLOCLIKE [[nodiscard]]
+#endif
+
template <class T> struct QTypedArrayData;
struct QArrayData
@@ -66,7 +74,7 @@ struct QArrayData
// Returns true if a detach is necessary before modifying the data
// This method is intentionally not const: if you want to know whether
// detaching is necessary, you should be in a non-const function already
- bool needsDetach() const noexcept
+ bool needsDetach() noexcept
{
return ref_.loadRelaxed() > 1;
}
@@ -78,13 +86,17 @@ struct QArrayData
return newSize;
}
- [[nodiscard]]
-#if defined(Q_CC_GNU)
- __attribute__((__malloc__))
-#endif
+ Q_DECL_MALLOCLIKE
static Q_CORE_EXPORT void *allocate(QArrayData **pdata, qsizetype objectSize, qsizetype alignment,
qsizetype capacity, AllocationOption option = QArrayData::KeepSize) noexcept;
- [[nodiscard]] static Q_CORE_EXPORT QPair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer,
+ Q_DECL_MALLOCLIKE
+ static Q_CORE_EXPORT void *allocate1(QArrayData **pdata, qsizetype capacity,
+ AllocationOption option = QArrayData::KeepSize) noexcept;
+ Q_DECL_MALLOCLIKE
+ static Q_CORE_EXPORT void *allocate2(QArrayData **pdata, qsizetype capacity,
+ AllocationOption option = QArrayData::KeepSize) noexcept;
+
+ [[nodiscard]] static Q_CORE_EXPORT std::pair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer,
qsizetype objectSize, qsizetype newCapacity, AllocationOption option) noexcept;
static Q_CORE_EXPORT void deallocate(QArrayData *data, qsizetype objectSize,
qsizetype alignment) noexcept;
@@ -92,30 +104,56 @@ struct QArrayData
Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::ArrayOptions)
+namespace QtPrivate {
+// QArrayData with strictest alignment requirements supported by malloc()
+#if defined(Q_PROCESSOR_X86_32) && defined(Q_CC_GNU)
+// GCC's definition is incorrect since GCC 8 (commit r240248 in SVN; commit
+// 63012d9a57edc950c5f30242d1e19318b5708060 in Git). This is applied to all
+// GCC-like compilers in case they decide to follow GCC's lead in being wrong.
+constexpr size_t MaxPrimitiveAlignment = 2 * sizeof(void *);
+#else
+constexpr size_t MaxPrimitiveAlignment = alignof(std::max_align_t);
+#endif
+
+struct alignas(MaxPrimitiveAlignment) AlignedQArrayData : QArrayData
+{
+};
+}
+
template <class T>
struct QTypedArrayData
: QArrayData
{
- struct AlignmentDummy { QArrayData header; T data; };
+ struct AlignmentDummy { QtPrivate::AlignedQArrayData header; T data; };
- [[nodiscard]] static QPair<QTypedArrayData *, T *> allocate(qsizetype capacity, AllocationOption option = QArrayData::KeepSize)
+ [[nodiscard]] static std::pair<QTypedArrayData *, T *> allocate(qsizetype capacity, AllocationOption option = QArrayData::KeepSize)
{
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
QArrayData *d;
- void *result = QArrayData::allocate(&d, sizeof(T), alignof(AlignmentDummy), capacity, option);
+ void *result;
+ if constexpr (sizeof(T) == 1) {
+ // necessarily, alignof(T) == 1
+ result = allocate1(&d, capacity, option);
+ } else if constexpr (sizeof(T) == 2) {
+ // alignof(T) may be 1, but that makes no difference
+ result = allocate2(&d, capacity, option);
+ } else {
+ result = QArrayData::allocate(&d, sizeof(T), alignof(AlignmentDummy), capacity, option);
+ }
#if __has_builtin(__builtin_assume_aligned)
+ // and yet we do offer results that have stricter alignment
result = __builtin_assume_aligned(result, Q_ALIGNOF(AlignmentDummy));
#endif
- return qMakePair(static_cast<QTypedArrayData *>(d), static_cast<T *>(result));
+ return {static_cast<QTypedArrayData *>(d), static_cast<T *>(result)};
}
- static QPair<QTypedArrayData *, T *>
+ static std::pair<QTypedArrayData *, T *>
reallocateUnaligned(QTypedArrayData *data, T *dataPointer, qsizetype capacity, AllocationOption option)
{
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
- QPair<QArrayData *, void *> pair =
+ std::pair<QArrayData *, void *> pair =
QArrayData::reallocateUnaligned(data, dataPointer, sizeof(T), capacity, option);
- return qMakePair(static_cast<QTypedArrayData *>(pair.first), static_cast<T *>(pair.second));
+ return {static_cast<QTypedArrayData *>(pair.first), static_cast<T *>(pair.second)};
}
static void deallocate(QArrayData *data) noexcept
@@ -132,6 +170,12 @@ struct QTypedArrayData
(quintptr(data) + sizeof(QArrayData) + alignment - 1) & ~(alignment - 1));
return static_cast<T *>(start);
}
+
+ constexpr static qsizetype max_size() noexcept
+ {
+ // -1 to deal with the pointer one-past-the-end
+ return (QtPrivate::MaxAllocSize - sizeof(QtPrivate::AlignedQArrayData) - 1) / sizeof(T);
+ }
};
namespace QtPrivate {
@@ -172,6 +216,8 @@ struct Q_CORE_EXPORT QContainerImplHelper
};
}
+#undef Q_DECL_MALLOCLIKE
+
QT_END_NAMESPACE
#endif // include guard
diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h
index bd8ead0a80..c3e9821e81 100644
--- a/src/corelib/tools/qarraydataops.h
+++ b/src/corelib/tools/qarraydataops.h
@@ -7,6 +7,7 @@
#include <QtCore/qarraydata.h>
#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/qnamespace.h>
#include <memory>
#include <new>
@@ -236,7 +237,7 @@ public:
if (it == end)
return result;
- QPodArrayOps<T> other{ Data::allocate(this->size), this->size };
+ QPodArrayOps<T> other(this->size);
Q_CHECK_PTR(other.data());
auto dest = other.begin();
// std::uninitialized_copy will fallback to ::memcpy/memmove()
@@ -960,6 +961,24 @@ public:
// b might be updated so use [b, n)
this->copyAppend(b, b + n);
}
+
+ void appendUninitialized(qsizetype newSize)
+ {
+ Q_ASSERT(this->isMutable());
+ Q_ASSERT(!this->isShared());
+ Q_ASSERT(newSize > this->size);
+ Q_ASSERT(newSize - this->size <= this->freeSpaceAtEnd());
+
+ T *const b = this->begin();
+ do {
+ auto ptr = b + this->size;
+
+ if constexpr (std::is_constructible_v<T, Qt::Initialization>)
+ new (ptr) T(Qt::Uninitialized);
+ else
+ new (ptr) T; // not T() -- default-construct
+ } while (++this->size != newSize);
+ }
};
} // namespace QtPrivate
diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h
index eb20330f0f..6657d40cf9 100644
--- a/src/corelib/tools/qarraydatapointer.h
+++ b/src/corelib/tools/qarraydatapointer.h
@@ -27,27 +27,39 @@ public:
typedef typename std::conditional<pass_parameter_by_value, T, const T &>::type parameter_type;
+ Q_NODISCARD_CTOR
constexpr QArrayDataPointer() noexcept
: d(nullptr), ptr(nullptr), size(0)
{
}
+ Q_NODISCARD_CTOR
QArrayDataPointer(const QArrayDataPointer &other) noexcept
: d(other.d), ptr(other.ptr), size(other.size)
{
ref();
}
+ Q_NODISCARD_CTOR
constexpr QArrayDataPointer(Data *header, T *adata, qsizetype n = 0) noexcept
: d(header), ptr(adata), size(n)
{
}
- explicit QArrayDataPointer(QPair<QTypedArrayData<T> *, T *> adata, qsizetype n = 0) noexcept
+ Q_NODISCARD_CTOR
+ explicit QArrayDataPointer(std::pair<QTypedArrayData<T> *, T *> adata, qsizetype n = 0) noexcept
: d(adata.first), ptr(adata.second), size(n)
{
}
+ Q_NODISCARD_CTOR explicit
+ QArrayDataPointer(qsizetype alloc, qsizetype n = 0,
+ QArrayData::AllocationOption option = QArrayData::KeepSize)
+ : QArrayDataPointer(Data::allocate(alloc, option), n)
+ {
+ }
+
+ Q_NODISCARD_CTOR
static QArrayDataPointer fromRawData(const T *rawData, qsizetype length) noexcept
{
Q_ASSERT(rawData || !length);
@@ -61,12 +73,12 @@ public:
return *this;
}
+ Q_NODISCARD_CTOR
QArrayDataPointer(QArrayDataPointer &&other) noexcept
- : d(other.d), ptr(other.ptr), size(other.size)
+ : d(std::exchange(other.d, nullptr)),
+ ptr(std::exchange(other.ptr, nullptr)),
+ size(std::exchange(other.size, 0))
{
- other.d = nullptr;
- other.ptr = nullptr;
- other.size = 0;
}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QArrayDataPointer)
@@ -320,13 +332,11 @@ public:
if constexpr (IsFwdIt) {
const qsizetype n = std::distance(first, last);
if (needsDetach() || n > constAllocatedCapacity()) {
- QArrayDataPointer allocated(Data::allocate(detachCapacity(n)));
- Q_CHECK_PTR(allocated.data());
+ QArrayDataPointer allocated(detachCapacity(n));
swap(allocated);
}
} else if (needsDetach()) {
- QArrayDataPointer allocated(Data::allocate(allocatedCapacity()));
- Q_CHECK_PTR(allocated.data());
+ QArrayDataPointer allocated(allocatedCapacity());
swap(allocated);
// We don't want to copy data that we know we'll overwrite
}
@@ -402,6 +412,26 @@ public:
size = dst - begin();
}
+ QArrayDataPointer sliced(qsizetype pos, qsizetype n) const &
+ {
+ QArrayDataPointer result(n);
+ std::uninitialized_copy_n(begin() + pos, n, result.begin());
+ result.size = n;
+ return result;
+ }
+
+ QArrayDataPointer sliced(qsizetype pos, qsizetype n) &&
+ {
+ if (needsDetach())
+ return sliced(pos, n);
+ T *newBeginning = begin() + pos;
+ std::destroy(begin(), newBeginning);
+ std::destroy(newBeginning + n, end());
+ setBegin(newBeginning);
+ size = n;
+ return std::move(*this);
+ }
+
// forwards from QArrayData
qsizetype allocatedCapacity() noexcept { return d ? d->allocatedCapacity() : 0; }
qsizetype constAllocatedCapacity() const noexcept { return d ? d->constAllocatedCapacity() : 0; }
diff --git a/src/corelib/tools/qatomicscopedvaluerollback_p.h b/src/corelib/tools/qatomicscopedvaluerollback.h
index 147156d585..8f653acba5 100644
--- a/src/corelib/tools/qatomicscopedvaluerollback_p.h
+++ b/src/corelib/tools/qatomicscopedvaluerollback.h
@@ -1,29 +1,21 @@
// Copyright (C) 2022 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
-#ifndef QATOMICSCOPEDVALUEROLLBACK_P_H
-#define QATOMICSCOPEDVALUEROLLBACK_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
-// file may change from version to version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qglobal.h>
+#ifndef QATOMICSCOPEDVALUEROLLBACK_H
+#define QATOMICSCOPEDVALUEROLLBACK_H
+
+#include <QtCore/qassert.h>
#include <QtCore/qatomic.h>
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/qtconfigmacros.h>
#include <atomic>
QT_BEGIN_NAMESPACE
template <typename T>
-class [[nodiscard]] QAtomicScopedValueRollback
+class QAtomicScopedValueRollback
{
std::atomic<T> &m_atomic;
T m_value;
@@ -31,7 +23,7 @@ class [[nodiscard]] QAtomicScopedValueRollback
Q_DISABLE_COPY_MOVE(QAtomicScopedValueRollback)
- constexpr std::memory_order store_part(std::memory_order mo) noexcept
+ static constexpr std::memory_order store_part(std::memory_order mo) noexcept
{
switch (mo) {
case std::memory_order_relaxed:
@@ -41,7 +33,25 @@ class [[nodiscard]] QAtomicScopedValueRollback
case std::memory_order_acq_rel: return std::memory_order_release;
case std::memory_order_seq_cst: return std::memory_order_seq_cst;
}
- // GCC 8.x does not tread __builtin_unreachable() as constexpr
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
+#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
+ // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
+ Q_UNREACHABLE();
+#endif
+ return std::memory_order_seq_cst;
+ }
+
+ static constexpr std::memory_order load_part(std::memory_order mo) noexcept
+ {
+ switch (mo) {
+ case std::memory_order_relaxed:
+ case std::memory_order_release: return std::memory_order_relaxed;
+ case std::memory_order_consume: return std::memory_order_consume;
+ case std::memory_order_acquire:
+ case std::memory_order_acq_rel: return std::memory_order_acquire;
+ case std::memory_order_seq_cst: return std::memory_order_seq_cst;
+ }
+ // GCC 8.x does not treat __builtin_unreachable() as constexpr
#if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
// NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
Q_UNREACHABLE();
@@ -52,11 +62,13 @@ public:
//
// std::atomic:
//
+ Q_NODISCARD_CTOR
explicit constexpr
QAtomicScopedValueRollback(std::atomic<T> &var,
std::memory_order mo = std::memory_order_seq_cst)
- : m_atomic(var), m_value(var.load(mo)), m_mo(mo) {}
+ : m_atomic(var), m_value(var.load(load_part(mo))), m_mo(mo) {}
+ Q_NODISCARD_CTOR
explicit constexpr
QAtomicScopedValueRollback(std::atomic<T> &var, T value,
std::memory_order mo = std::memory_order_seq_cst)
@@ -65,11 +77,13 @@ public:
//
// Q(Basic)AtomicInteger:
//
+ Q_NODISCARD_CTOR
explicit constexpr
QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var,
std::memory_order mo = std::memory_order_seq_cst)
: QAtomicScopedValueRollback(var._q_value, mo) {}
+ Q_NODISCARD_CTOR
explicit constexpr
QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var, T value,
std::memory_order mo = std::memory_order_seq_cst)
@@ -78,30 +92,36 @@ public:
//
// Q(Basic)AtomicPointer:
//
+ Q_NODISCARD_CTOR
explicit constexpr
QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var,
std::memory_order mo = std::memory_order_seq_cst)
: QAtomicScopedValueRollback(var._q_value, mo) {}
+ Q_NODISCARD_CTOR
explicit constexpr
QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var, T value,
std::memory_order mo = std::memory_order_seq_cst)
: QAtomicScopedValueRollback(var._q_value, value, mo) {}
-#if __cpp_constexpr >= 201907L
- constexpr
-#endif
~QAtomicScopedValueRollback()
{
m_atomic.store(m_value, store_part(m_mo));
}
- constexpr void commit()
+ void commit()
{
- m_value = m_atomic.load(m_mo);
+ m_value = m_atomic.load(load_part(m_mo));
}
};
+template <typename T>
+QAtomicScopedValueRollback(QBasicAtomicPointer<T> &)
+ -> QAtomicScopedValueRollback<T*>;
+template <typename T>
+QAtomicScopedValueRollback(QBasicAtomicPointer<T> &, std::memory_order)
+ -> QAtomicScopedValueRollback<T*>;
+
QT_END_NAMESPACE
-#endif // QATOMICASCOPEDVALUEROLLBACK_P_H
+#endif // QATOMICASCOPEDVALUEROLLBACK_H
diff --git a/src/corelib/tools/qatomicscopedvaluerollback.qdoc b/src/corelib/tools/qatomicscopedvaluerollback.qdoc
new file mode 100644
index 0000000000..8c8161cb35
--- /dev/null
+++ b/src/corelib/tools/qatomicscopedvaluerollback.qdoc
@@ -0,0 +1,123 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAtomicScopedValueRollback
+ \inmodule QtCore
+ \brief Provides a QScopedValueRollback for atomic variables.
+ \ingroup misc
+ \ingroup tools
+ \since 6.7
+
+ The QAtomicScopedValueRollback class resets an atomic variable to its
+ prior value on destruction. It can be used to revert state when an
+ exception is thrown without the need to write try-catch blocks.
+
+ It can also be used to manage variables that are temporarily set, such as
+ reentrancy guards. By using this class, the variable will be reset whether the
+ function is exited normally, exited early by a return statement, or exited by
+ an exception.
+
+ The class works on std::atomic and the Qt atomic classes: QBasicAtomicInteger,
+ \l QAtomicInteger, \l QAtomicInt, QBasicAtomicPointer and \l QAtomicPointer.
+
+ \target Memory Order
+ The memory accesses to the atomic variable \a var are specified using the value
+ of \c mo. The memory order follows this mapping:
+ \br
+ \list
+ \li When writing to the atomic variable:
+ \list
+ \li An acquire ordering performs a relaxed operation instead.
+ \li A hybrid acquire-release ordering performs a release operation instead.
+ \endlist
+ \li When reading from the atomic variable:
+ \list
+ \li A release ordering performs a relaxed operation instead.
+ \li A consume ordering performs a consume operation.
+ \li A hybrid acquire-release ordering performs an acquire operation instead.
+ \endlist
+ \endlist
+ \br
+ Otherwise, the default memory order is sequential consistent ordering.
+
+ \note You should never name the template arguments explicitly, but exclusively
+ use Class Template Argument Deduction (CTAD) and let the compiler pick the
+ template argument.
+
+ \note There is a chance that other threads modify the variable too, which means
+ you may lose updates performed by other threads between the call to the
+ QAtomicScopedValueRollback constructor and commit() or between commit() and the
+ destructor.
+
+ \sa QScopedValueRollback
+*/
+
+/*!
+ \fn template <typename T> QAtomicScopedValueRollback<T>::QAtomicScopedValueRollback(std::atomic<T> &var, std::memory_order mo = std::memory_order_seq_cst)
+ \fn template <typename T> QAtomicScopedValueRollback<T>::QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var, std::memory_order mo = std::memory_order_seq_cst)
+ \fn template <typename T> QAtomicScopedValueRollback<T>::QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var, std::memory_order mo = std::memory_order_seq_cst)
+
+ Records the value of \a var in order to restore it on destruction.
+
+ This is equivalent to:
+ \code
+ T old_value = var.load(mo);
+ // And in the destructor: var.store(old_value, mo);
+ \endcode
+ The \c{mo} adjustment for the load is described in the \l {Memory Order} section.
+*/
+
+/*!
+ \fn template <typename T> QAtomicScopedValueRollback<T>::QAtomicScopedValueRollback(std::atomic<T> &var, T value, std::memory_order mo = std::memory_order_seq_cst)
+ \fn template <typename T> QAtomicScopedValueRollback<T>::QAtomicScopedValueRollback(QBasicAtomicInteger<T> &var, T value, std::memory_order mo = std::memory_order_seq_cst)
+ \fn template <typename T> QAtomicScopedValueRollback<T>::QAtomicScopedValueRollback(QBasicAtomicPointer<std::remove_pointer_t<T>> &var, T value, std::memory_order mo = std::memory_order_seq_cst)
+
+ Assigns \a value to \a var and stores the prior value of \a var internally for
+ reverting on destruction.
+
+ This is equivalent to:
+ \code
+ T old_value = var.exchange(new_value, mo);
+ // And in the destructor: var.store(old_value, mo);
+ \endcode
+*/
+
+/*!
+ \fn template <typename T> QAtomicScopedValueRollback<T>::~QAtomicScopedValueRollback()
+
+ Restores the stored value that was current at construction time, or
+ at the last call to commit(), to the managed variable.
+
+ This is equivalent to:
+ \code
+ // In the constructor: T old_value = var.load(mo);
+ // or: T old_value = exchange(new_value, mo);
+ var.store(old_value, mo);
+ \endcode
+ Where \c{mo} is the same as the one initially passed to the constructor.
+ See \l{Memory Order} for the meaning of \c{mo}.
+*/
+
+/*!
+ \fn template <typename T> void QAtomicScopedValueRollback<T>::commit()
+
+ Updates the stored value to the managed variable's current value, loaded
+ with the same memory order as on construction.
+
+ This updated value will be restored on destruction, instead of the original
+ prior value.
+
+ This is equivalent to:
+ \code
+ // Given constructor: T old_value = var.load(mo);
+ old_value = var.load(mo); // referesh it
+ // And, in the destructor: var.store(old_value, mo);
+ \endcode
+ Where \c{mo} is the same as the one initially passed to the constructor.
+ See \l{Memory Order} for the meaning of \c{mo}.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp
index 5c48b3b608..e4276d383d 100644
--- a/src/corelib/tools/qbitarray.cpp
+++ b/src/corelib/tools/qbitarray.cpp
@@ -7,6 +7,9 @@
#include <qdatastream.h>
#include <qdebug.h>
#include <qendian.h>
+
+#include <limits>
+
#include <string.h>
QT_BEGIN_NAMESPACE
@@ -20,6 +23,8 @@ QT_BEGIN_NAMESPACE
\ingroup shared
\reentrant
+ \compares equality
+
A QBitArray is an array that gives access to individual bits and
provides operators (\l{operator&()}{AND}, \l{operator|()}{OR},
\l{operator^()}{XOR}, and \l{operator~()}{NOT}) that work on
@@ -74,6 +79,7 @@ QT_BEGIN_NAMESPACE
\sa QByteArray, QList
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
\fn QBitArray::QBitArray(QBitArray &&other)
@@ -82,6 +88,7 @@ QT_BEGIN_NAMESPACE
\since 5.2
*/
+#endif
/*! \fn QBitArray::QBitArray()
@@ -104,22 +111,39 @@ QT_BEGIN_NAMESPACE
* inline qsizetype size() const { return (d.size() << 3) - *d.constData(); }
*/
+static constexpr qsizetype storage_size(qsizetype size)
+{
+ // avoid overflow when adding 7, by doing the arithmetic in unsigned space:
+ return qsizetype((size_t(size) + 7) / 8);
+}
+
+static constexpr qsizetype allocation_size(qsizetype size)
+{
+ return size <= 0 ? 0 : storage_size(size) + 1;
+}
+
+static void adjust_head_and_tail(char *data, qsizetype storageSize, qsizetype logicalSize)
+{
+ quint8 *c = reinterpret_cast<quint8 *>(data);
+ // store the difference between storage and logical size in d[0]:
+ *c = quint8(size_t(storageSize) * 8 - logicalSize);
+ // reset unallocated bits to 0:
+ if (logicalSize & 7)
+ *(c + 1 + logicalSize / 8) &= (1 << (logicalSize & 7)) - 1;
+}
+
/*!
Constructs a bit array containing \a size bits. The bits are
initialized with \a value, which defaults to false (0).
*/
QBitArray::QBitArray(qsizetype size, bool value)
- : d(size <= 0 ? 0 : 1 + (size + 7) / 8, Qt::Uninitialized)
+ : d(allocation_size(size), value ? 0xFF : 0x00)
{
Q_ASSERT_X(size >= 0, "QBitArray::QBitArray", "Size must be greater than or equal to 0.");
if (size <= 0)
return;
- uchar *c = reinterpret_cast<uchar *>(d.data());
- memset(c + 1, value ? 0xff : 0, d.size() - 1);
- *c = d.size() * 8 - size;
- if (value && size && size & 7)
- *(c + 1 + size / 8) &= (1 << (size & 7)) - 1;
+ adjust_head_and_tail(d.data(), d.size(), size);
}
/*! \fn qsizetype QBitArray::size() const
@@ -183,17 +207,12 @@ qsizetype QBitArray::count(bool on) const
*/
void QBitArray::resize(qsizetype size)
{
- if (!size) {
+ Q_ASSERT_X(size >= 0, "QBitArray::resize", "Size must be greater than or equal to 0.");
+ if (size <= 0) {
d.resize(0);
} else {
- qsizetype s = d.size();
- d.resize(1 + (size + 7) / 8);
- uchar *c = reinterpret_cast<uchar *>(d.data());
- if (size > (s << 3))
- memset(c + s, 0, d.size() - s);
- else if (size & 7)
- *(c + 1 + size / 8) &= (1 << (size & 7)) - 1;
- *c = d.size() * 8 - size;
+ d.resize(allocation_size(size), 0x00);
+ adjust_head_and_tail(d.data(), d.size(), size);
}
}
@@ -289,20 +308,15 @@ void QBitArray::fill(bool value, qsizetype begin, qsizetype end)
*/
QBitArray QBitArray::fromBits(const char *data, qsizetype size)
{
+ Q_ASSERT_X(size >= 0, "QBitArray::fromBits", "Size must be greater than or equal to 0.");
QBitArray result;
- if (size == 0)
+ if (size <= 0)
return result;
- qsizetype nbytes = (size + 7) / 8;
-
- result.d = QByteArray(nbytes + 1, Qt::Uninitialized);
- char *bits = result.d.data();
- memcpy(bits + 1, data, nbytes);
-
- // clear any unused bits from the last byte
- if (size & 7)
- bits[nbytes] &= 0xffU >> (8 - (size & 7));
- *bits = result.d.size() * 8 - size;
+ auto &d = result.d;
+ d.resize(allocation_size(size));
+ memcpy(d.data() + 1, data, d.size() - 1);
+ adjust_head_and_tail(d.data(), d.size(), size);
return result;
}
@@ -453,6 +467,7 @@ quint32 QBitArray::toUInt32(QSysInfo::Endian endianness, bool *ok) const noexcep
\overload
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*! \fn QBitArray::QBitArray(const QBitArray &other) noexcept
Constructs a copy of \a other.
@@ -477,6 +492,7 @@ quint32 QBitArray::toUInt32(QSysInfo::Endian endianness, bool *ok) const noexcep
Moves \a other to this bit array and returns a reference to
this bit array.
*/
+#endif // Qt 6
/*! \fn void QBitArray::swap(QBitArray &other)
\since 4.8
@@ -485,23 +501,145 @@ quint32 QBitArray::toUInt32(QSysInfo::Endian endianness, bool *ok) const noexcep
fast and never fails.
*/
-/*! \fn bool QBitArray::operator==(const QBitArray &other) const
+/*! \fn bool QBitArray::operator==(const QBitArray &lhs, const QBitArray &rhs)
- Returns \c true if \a other is equal to this bit array; otherwise
+ Returns \c true if \a lhs is equal to \a rhs bit array; otherwise
returns \c false.
\sa operator!=()
*/
-/*! \fn bool QBitArray::operator!=(const QBitArray &other) const
+/*! \fn bool QBitArray::operator!=(const QBitArray &lhs, const QBitArray &rhs)
- Returns \c true if \a other is not equal to this bit array;
+ Returns \c true if \a lhs is not equal to \a rhs bit array;
otherwise returns \c false.
\sa operator==()
*/
+// Returns a new QBitArray that has the same size as the bigger of \a a1 and
+// \a a2, but whose contents are uninitialized.
+static QBitArray sizedForOverwrite(const QBitArray &a1, const QBitArray &a2)
+{
+ QBitArray result;
+ const QByteArrayData &d1 = a1.data_ptr();
+ const QByteArrayData &d2 = a2.data_ptr();
+ qsizetype n1 = d1.size;
+ qsizetype n2 = d2.size;
+ qsizetype n = qMax(n1, n2);
+
+ QByteArrayData bytes(n, n);
+
+ // initialize the count of bits in the last byte (see construction note)
+ if (n1 > n2)
+ *bytes.ptr = *d1.ptr;
+ else if (n2 > n1)
+ *bytes.ptr = *d2.ptr;
+ else if (n1) // n1 == n2
+ *bytes.ptr = qMin(*d1.ptr, *d2.ptr);
+
+ result.data_ptr() = std::move(bytes);
+ return result;
+}
+
+template <typename BitwiseOp> static Q_NEVER_INLINE
+QBitArray &performBitwiseOperationHelper(QBitArray &out, const QBitArray &a1,
+ const QBitArray &a2, BitwiseOp op)
+{
+ const QByteArrayData &d1 = a1.data_ptr();
+ const QByteArrayData &d2 = a2.data_ptr();
+
+ // Sizes in bytes (including the initial bit difference counter)
+ qsizetype n1 = d1.size;
+ qsizetype n2 = d2.size;
+ Q_ASSERT(out.data_ptr().size == qMax(n1, n2));
+ Q_ASSERT(out.data_ptr().size == 0 || !out.data_ptr().needsDetach());
+
+ // Bypass QByteArray's emptiness verification; we won't dereference
+ // these pointers if their size is zero.
+ auto dst = reinterpret_cast<uchar *>(out.data_ptr().data());
+ auto p1 = reinterpret_cast<const uchar *>(d1.data());
+ auto p2 = reinterpret_cast<const uchar *>(d2.data());
+
+ // Main: perform the operation in the range where both arrays have data
+ if (n1 < n2) {
+ std::swap(n1, n2);
+ std::swap(p1, p2);
+ }
+ for (qsizetype i = 1; i < n2; ++i)
+ dst[i] = op(p1[i], p2[i]);
+
+ // Tail: operate as if both arrays had the same data by padding zeroes to
+ // the end of the shorter of the two (for std::bit_or and std::bit_xor, this is
+ // a memmove; for std::bit_and, it's memset to 0).
+ for (qsizetype i = qMax(n2, qsizetype(1)); i < n1; ++i)
+ dst[i] = op(p1[i], uchar(0));
+
+ return out;
+}
+
+template <typename BitwiseOp> static Q_NEVER_INLINE
+QBitArray &performBitwiseOperationInCopy(QBitArray &self, const QBitArray &other, BitwiseOp op)
+{
+ QBitArray tmp(std::move(self));
+ self = sizedForOverwrite(tmp, other);
+ return performBitwiseOperationHelper(self, tmp, other, op);
+}
+
+template <typename BitwiseOp> static Q_NEVER_INLINE
+QBitArray &performBitwiseOperationInPlace(QBitArray &self, const QBitArray &other, BitwiseOp op)
+{
+ if (self.size() < other.size())
+ self.resize(other.size());
+ return performBitwiseOperationHelper(self, self, other, op);
+}
+
+template <typename BitwiseOp> static
+QBitArray &performBitwiseOperation(QBitArray &self, const QBitArray &other, BitwiseOp op)
+{
+ if (self.data_ptr().needsDetach())
+ return performBitwiseOperationInCopy(self, other, op);
+ return performBitwiseOperationInPlace(self, other, op);
+}
+
+// SCARY helper
+enum { InCopy, InPlace };
+static auto prepareForBitwiseOperation(QBitArray &self, QBitArray &other)
+{
+ QByteArrayData &d1 = self.data_ptr();
+ QByteArrayData &d2 = other.data_ptr();
+ bool detached1 = !d1.needsDetach();
+ bool detached2 = !d2.needsDetach();
+ if (!detached1 && !detached2)
+ return InCopy;
+
+ // at least one of the two is detached, we'll reuse its buffer
+ bool swap = false;
+ if (detached1 && detached2) {
+ // both are detached, so choose the larger of the two
+ swap = d1.allocatedCapacity() < d2.allocatedCapacity();
+ } else if (detached2) {
+ // we can re-use other's buffer but not self's, so swap the two
+ swap = true;
+ }
+ if (swap)
+ self.swap(other);
+ return InPlace;
+}
+
+template <typename BitwiseOp> static
+QBitArray &performBitwiseOperation(QBitArray &self, QBitArray &other, BitwiseOp op)
+{
+ auto choice = prepareForBitwiseOperation(self, other);
+ if (choice == InCopy)
+ return performBitwiseOperationInCopy(self, other, std::move(op));
+ return performBitwiseOperationInPlace(self, other, std::move(op));
+}
+
/*!
+ \fn QBitArray &QBitArray::operator&=(const QBitArray &other)
+ \fn QBitArray &QBitArray::operator&=(QBitArray &&other)
+
Performs the AND operation between all bits in this bit array and
\a other. Assigns the result to this bit array, and returns a
reference to it.
@@ -516,21 +654,20 @@ quint32 QBitArray::toUInt32(QSysInfo::Endian endianness, bool *ok) const noexcep
\sa operator&(), operator|=(), operator^=(), operator~()
*/
+QBitArray &QBitArray::operator&=(QBitArray &&other)
+{
+ return performBitwiseOperation(*this, other, std::bit_and<uchar>());
+}
+
QBitArray &QBitArray::operator&=(const QBitArray &other)
{
- resize(qMax(size(), other.size()));
- uchar *a1 = reinterpret_cast<uchar *>(d.data()) + 1;
- const uchar *a2 = reinterpret_cast<const uchar *>(other.d.constData()) + 1;
- qsizetype n = other.d.size() - 1;
- qsizetype p = d.size() - 1 - n;
- while (n-- > 0)
- *a1++ &= *a2++;
- while (p-- > 0)
- *a1++ = 0;
- return *this;
+ return performBitwiseOperation(*this, other, std::bit_and<uchar>());
}
/*!
+ \fn QBitArray &QBitArray::operator|=(const QBitArray &other)
+ \fn QBitArray &QBitArray::operator|=(QBitArray &&other)
+
Performs the OR operation between all bits in this bit array and
\a other. Assigns the result to this bit array, and returns a
reference to it.
@@ -545,18 +682,20 @@ QBitArray &QBitArray::operator&=(const QBitArray &other)
\sa operator|(), operator&=(), operator^=(), operator~()
*/
+QBitArray &QBitArray::operator|=(QBitArray &&other)
+{
+ return performBitwiseOperation(*this, other, std::bit_or<uchar>());
+}
+
QBitArray &QBitArray::operator|=(const QBitArray &other)
{
- resize(qMax(size(), other.size()));
- uchar *a1 = reinterpret_cast<uchar *>(d.data()) + 1;
- const uchar *a2 = reinterpret_cast<const uchar *>(other.d.constData()) + 1;
- qsizetype n = other.d.size() - 1;
- while (n-- > 0)
- *a1++ |= *a2++;
- return *this;
+ return performBitwiseOperation(*this, other, std::bit_or<uchar>());
}
/*!
+ \fn QBitArray &QBitArray::operator^=(const QBitArray &other)
+ \fn QBitArray &QBitArray::operator^=(QBitArray &&other)
+
Performs the XOR operation between all bits in this bit array and
\a other. Assigns the result to this bit array, and returns a
reference to it.
@@ -571,20 +710,20 @@ QBitArray &QBitArray::operator|=(const QBitArray &other)
\sa operator^(), operator&=(), operator|=(), operator~()
*/
+QBitArray &QBitArray::operator^=(QBitArray &&other)
+{
+ return performBitwiseOperation(*this, other, std::bit_xor<uchar>());
+}
+
QBitArray &QBitArray::operator^=(const QBitArray &other)
{
- resize(qMax(size(), other.size()));
- uchar *a1 = reinterpret_cast<uchar *>(d.data()) + 1;
- const uchar *a2 = reinterpret_cast<const uchar *>(other.d.constData()) + 1;
- qsizetype n = other.d.size() - 1;
- while (n-- > 0)
- *a1++ ^= *a2++;
- return *this;
+ return performBitwiseOperation(*this, other, std::bit_xor<uchar>());
}
/*!
- Returns a bit array that contains the inverted bits of this bit
- array.
+ \fn QBitArray QBitArray::operator~(QBitArray a)
+ Returns a bit array that contains the inverted bits of the bit
+ array \a a.
Example:
\snippet code/src_corelib_tools_qbitarray.cpp 11
@@ -592,24 +731,42 @@ QBitArray &QBitArray::operator^=(const QBitArray &other)
\sa operator&(), operator|(), operator^()
*/
-QBitArray QBitArray::operator~() const
+Q_NEVER_INLINE QBitArray QBitArray::inverted_inplace() &&
{
- qsizetype sz = size();
- QBitArray a(sz);
- const uchar *a1 = reinterpret_cast<const uchar *>(d.constData()) + 1;
- uchar *a2 = reinterpret_cast<uchar *>(a.d.data()) + 1;
- qsizetype n = d.size() - 1;
-
- while (n-- > 0)
- *a2++ = ~*a1++;
-
- if (sz && sz % 8)
- *(a2 - 1) &= (1 << (sz % 8)) - 1;
- return a;
+ qsizetype n = d.size();
+ uchar *dst = reinterpret_cast<uchar *>(data_ptr().data());
+ const uchar *src = dst;
+ QBitArray result([&] {
+ if (d.isDetached() || n == 0)
+ return std::move(d.data_ptr()); // invert in-place
+
+ QByteArrayData tmp(n, n);
+ dst = reinterpret_cast<uchar *>(tmp.data());
+ return tmp;
+ }());
+
+ uchar bitdiff = 8;
+ if (n)
+ bitdiff = dst[0] = src[0]; // copy the count of bits in the last byte
+
+ for (qsizetype i = 1; i < n; ++i)
+ dst[i] = ~src[i];
+
+ if (int tailCount = 16 - bitdiff; tailCount != 8) {
+ // zero the bits beyond our size in the last byte
+ Q_ASSERT(n > 1);
+ uchar tailMask = (1U << tailCount) - 1;
+ dst[n - 1] &= tailMask;
+ }
+
+ return result;
}
/*!
- \relates QBitArray
+ \fn QBitArray QBitArray::operator&(const QBitArray &a1, const QBitArray &a2)
+ \fn QBitArray QBitArray::operator&(QBitArray &&a1, const QBitArray &a2)
+ \fn QBitArray QBitArray::operator&(const QBitArray &a1, QBitArray &&a2)
+ \fn QBitArray QBitArray::operator&(QBitArray &&a1, QBitArray &&a2)
Returns a bit array that is the AND of the bit arrays \a a1 and \a
a2.
@@ -626,13 +783,16 @@ QBitArray QBitArray::operator~() const
QBitArray operator&(const QBitArray &a1, const QBitArray &a2)
{
- QBitArray tmp = a1;
- tmp &= a2;
+ QBitArray tmp = sizedForOverwrite(a1, a2);
+ performBitwiseOperationHelper(tmp, a1, a2, std::bit_and<uchar>());
return tmp;
}
/*!
- \relates QBitArray
+ \fn QBitArray QBitArray::operator|(const QBitArray &a1, const QBitArray &a2)
+ \fn QBitArray QBitArray::operator|(QBitArray &&a1, const QBitArray &a2)
+ \fn QBitArray QBitArray::operator|(const QBitArray &a1, QBitArray &&a2)
+ \fn QBitArray QBitArray::operator|(QBitArray &&a1, QBitArray &&a2)
Returns a bit array that is the OR of the bit arrays \a a1 and \a
a2.
@@ -649,13 +809,16 @@ QBitArray operator&(const QBitArray &a1, const QBitArray &a2)
QBitArray operator|(const QBitArray &a1, const QBitArray &a2)
{
- QBitArray tmp = a1;
- tmp |= a2;
+ QBitArray tmp = sizedForOverwrite(a1, a2);
+ performBitwiseOperationHelper(tmp, a1, a2, std::bit_or<uchar>());
return tmp;
}
/*!
- \relates QBitArray
+ \fn QBitArray QBitArray::operator^(const QBitArray &a1, const QBitArray &a2)
+ \fn QBitArray QBitArray::operator^(QBitArray &&a1, const QBitArray &a2)
+ \fn QBitArray QBitArray::operator^(const QBitArray &a1, QBitArray &&a2)
+ \fn QBitArray QBitArray::operator^(QBitArray &&a1, QBitArray &&a2)
Returns a bit array that is the XOR of the bit arrays \a a1 and \a
a2.
@@ -672,8 +835,8 @@ QBitArray operator|(const QBitArray &a1, const QBitArray &a2)
QBitArray operator^(const QBitArray &a1, const QBitArray &a2)
{
- QBitArray tmp = a1;
- tmp ^= a2;
+ QBitArray tmp = sizedForOverwrite(a1, a2);
+ performBitwiseOperationHelper(tmp, a1, a2, std::bit_xor<uchar>());
return tmp;
}
@@ -733,19 +896,19 @@ QBitArray operator^(const QBitArray &a1, const QBitArray &a2)
QDataStream &operator<<(QDataStream &out, const QBitArray &ba)
{
+ const qsizetype len = ba.size();
if (out.version() < QDataStream::Qt_6_0) {
- quint32 len = ba.size();
- out << len;
- if (len > 0)
- out.writeRawData(ba.d.constData() + 1, ba.d.size() - 1);
- return out;
+ if (Q_UNLIKELY(len > qsizetype{(std::numeric_limits<qint32>::max)()})) {
+ out.setStatus(QDataStream::Status::SizeLimitExceeded);
+ return out;
+ }
+ out << quint32(len);
} else {
- quint64 len = ba.size();
- out << len;
- if (len > 0)
- out.writeRawData(ba.d.constData() + 1, ba.d.size() - 1);
- return out;
+ out << quint64(len);
}
+ if (len > 0)
+ out.writeRawData(ba.d.data() + 1, ba.d.size() - 1);
+ return out;
}
/*!
@@ -763,10 +926,18 @@ QDataStream &operator>>(QDataStream &in, QBitArray &ba)
if (in.version() < QDataStream::Qt_6_0) {
quint32 tmp;
in >> tmp;
+ if (Q_UNLIKELY(tmp > quint32((std::numeric_limits<qint32>::max)()))) {
+ in.setStatus(QDataStream::ReadCorruptData);
+ return in;
+ }
len = tmp;
} else {
quint64 tmp;
in >> tmp;
+ if (Q_UNLIKELY(tmp > quint64((std::numeric_limits<qsizetype>::max)()))) {
+ in.setStatus(QDataStream::Status::SizeLimitExceeded);
+ return in;
+ }
len = tmp;
}
if (len == 0) {
@@ -775,7 +946,7 @@ QDataStream &operator>>(QDataStream &in, QBitArray &ba)
}
const qsizetype Step = 8 * 1024 * 1024;
- qsizetype totalBytes = (len + 7) / 8;
+ const qsizetype totalBytes = storage_size(len);
qsizetype allocated = 0;
while (allocated < totalBytes) {
@@ -789,14 +960,13 @@ QDataStream &operator>>(QDataStream &in, QBitArray &ba)
allocated += blockSize;
}
- qsizetype paddingMask = ~((0x1 << (len & 0x7)) - 1);
- if (paddingMask != ~0x0 && (ba.d.constData()[ba.d.size() - 1] & paddingMask)) {
+ const auto fromStream = ba.d.back();
+ adjust_head_and_tail(ba.d.data(), ba.d.size(), len);
+ if (ba.d.back() != fromStream) {
ba.clear();
in.setStatus(QDataStream::ReadCorruptData);
return in;
}
-
- *ba.d.data() = ba.d.size() * 8 - len;
return in;
}
#endif // QT_NO_DATASTREAM
diff --git a/src/corelib/tools/qbitarray.h b/src/corelib/tools/qbitarray.h
index e724aea598..b9c36b5320 100644
--- a/src/corelib/tools/qbitarray.h
+++ b/src/corelib/tools/qbitarray.h
@@ -11,25 +11,70 @@ QT_BEGIN_NAMESPACE
class QBitRef;
class Q_CORE_EXPORT QBitArray
{
+ Q_CORE_EXPORT friend QBitArray operator&(const QBitArray &a1, const QBitArray &a2);
+ friend QBitArray operator&(QBitArray &&a1, const QBitArray &a2)
+ { return a1 &= a2; }
+ friend QBitArray operator&(const QBitArray &a1, QBitArray &&a2)
+ { return a2 &= a1; }
+ friend QBitArray operator&(QBitArray &&a1, QBitArray &&a2)
+ { return a1 &= a2; }
+
+ Q_CORE_EXPORT friend QBitArray operator|(const QBitArray &a1, const QBitArray &a2);
+ friend QBitArray operator|(QBitArray &&a1, const QBitArray &a2)
+ { return a1 |= a2; }
+ friend QBitArray operator|(const QBitArray &a1, QBitArray &&a2)
+ { return a2 |= a1; }
+ friend QBitArray operator|(QBitArray &&a1, QBitArray &&a2)
+ { return a1 |= a2; }
+
+ Q_CORE_EXPORT friend QBitArray operator^(const QBitArray &a1, const QBitArray &a2);
+ friend QBitArray operator^(QBitArray &&a1, const QBitArray &a2)
+ { return a1 ^= a2; }
+ friend QBitArray operator^(const QBitArray &a1, QBitArray &&a2)
+ { return a2 ^= a1; }
+ friend QBitArray operator^(QBitArray &&a1, QBitArray &&a2)
+ { return a1 ^= a2; }
+
#ifndef QT_NO_DATASTREAM
friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QBitArray &);
friend Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QBitArray &);
#endif
friend Q_CORE_EXPORT size_t qHash(const QBitArray &key, size_t seed) noexcept;
+ friend QBitArray operator~(QBitArray a)
+ { return std::move(a).inverted_inplace(); }
QByteArray d;
+ QBitArray(QByteArrayData &&dd) : d(std::move(dd)) {}
+
+ template <typename BitArray> static auto bitLocation(BitArray &ba, qsizetype i)
+ {
+ Q_ASSERT(size_t(i) < size_t(ba.size()));
+ struct R {
+ decltype(ba.d[1]) byte;
+ uchar bitMask;
+ };
+ qsizetype byteIdx = i >> 3;
+ qsizetype bitIdx = i & 7;
+ return R{ ba.d[1 + byteIdx], uchar(1U << bitIdx) };
+ }
+
+ QBitArray inverted_inplace() &&;
+
public:
inline QBitArray() noexcept {}
explicit QBitArray(qsizetype size, bool val = false);
+ // Rule Of Zero applies
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
QBitArray(const QBitArray &other) noexcept : d(other.d) {}
inline QBitArray &operator=(const QBitArray &other) noexcept { d = other.d; return *this; }
inline QBitArray(QBitArray &&other) noexcept : d(std::move(other.d)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QBitArray)
+#endif // Qt 6
void swap(QBitArray &other) noexcept { d.swap(other.d); }
- inline qsizetype size() const { return (d.size() << 3) - *d.constData(); }
- inline qsizetype count() const { return (d.size() << 3) - *d.constData(); }
+ qsizetype size() const { return qsizetype((size_t(d.size()) << 3) - *d.constData()); }
+ qsizetype count() const { return size(); }
qsizetype count(bool on) const;
inline bool isEmpty() const { return d.isEmpty(); }
@@ -41,25 +86,43 @@ public:
inline bool isDetached() const { return d.isDetached(); }
inline void clear() { d.clear(); }
- bool testBit(qsizetype i) const;
- void setBit(qsizetype i);
- void setBit(qsizetype i, bool val);
- void clearBit(qsizetype i);
- bool toggleBit(qsizetype i);
-
- bool at(qsizetype i) const;
- QBitRef operator[](qsizetype i);
- bool operator[](qsizetype i) const;
-
+ bool testBit(qsizetype i) const
+ { auto r = bitLocation(*this, i); return r.byte & r.bitMask; }
+ void setBit(qsizetype i)
+ { auto r = bitLocation(*this, i); r.byte |= r.bitMask; }
+ void setBit(qsizetype i, bool val)
+ { if (val) setBit(i); else clearBit(i); }
+ void clearBit(qsizetype i)
+ { auto r = bitLocation(*this, i); r.byte &= ~r.bitMask; }
+ bool toggleBit(qsizetype i)
+ {
+ auto r = bitLocation(*this, i);
+ bool cl = r.byte & r.bitMask;
+ r.byte ^= r.bitMask;
+ return cl;
+ }
+
+ bool at(qsizetype i) const { return testBit(i); }
+ inline QBitRef operator[](qsizetype i);
+ bool operator[](qsizetype i) const { return testBit(i); }
+
+ QBitArray &operator&=(QBitArray &&);
+ QBitArray &operator|=(QBitArray &&);
+ QBitArray &operator^=(QBitArray &&);
QBitArray &operator&=(const QBitArray &);
QBitArray &operator|=(const QBitArray &);
QBitArray &operator^=(const QBitArray &);
+#if QT_CORE_REMOVED_SINCE(6, 7)
QBitArray operator~() const;
+#endif
- inline bool operator==(const QBitArray &other) const { return d == other.d; }
- inline bool operator!=(const QBitArray &other) const { return d != other.d; }
+#if QT_CORE_REMOVED_SINCE(6, 8)
+ inline bool operator==(const QBitArray &other) const { return comparesEqual(d, other.d); }
+ inline bool operator!=(const QBitArray &other) const { return !operator==(other); }
+#endif
- inline bool fill(bool val, qsizetype size = -1);
+ bool fill(bool aval, qsizetype asize = -1)
+ { *this = QBitArray((asize < 0 ? this->size() : asize), aval); return true; }
void fill(bool val, qsizetype first, qsizetype last);
inline void truncate(qsizetype pos) { if (pos < size()) resize(pos); }
@@ -72,39 +135,17 @@ public:
public:
typedef QByteArray::DataPointer DataPtr;
inline DataPtr &data_ptr() { return d.data_ptr(); }
-};
+ inline const DataPtr &data_ptr() const { return d.data_ptr(); }
-inline bool QBitArray::fill(bool aval, qsizetype asize)
-{ *this = QBitArray((asize < 0 ? this->size() : asize), aval); return true; }
-
-Q_CORE_EXPORT QBitArray operator&(const QBitArray &, const QBitArray &);
-Q_CORE_EXPORT QBitArray operator|(const QBitArray &, const QBitArray &);
-Q_CORE_EXPORT QBitArray operator^(const QBitArray &, const QBitArray &);
-
-inline bool QBitArray::testBit(qsizetype i) const
-{ Q_ASSERT(size_t(i) < size_t(size()));
- return (*(reinterpret_cast<const uchar*>(d.constData())+1+(i>>3)) & (1 << (i & 7))) != 0; }
-
-inline void QBitArray::setBit(qsizetype i)
-{ Q_ASSERT(size_t(i) < size_t(size()));
- *(reinterpret_cast<uchar*>(d.data())+1+(i>>3)) |= uchar(1 << (i & 7)); }
-
-inline void QBitArray::clearBit(qsizetype i)
-{ Q_ASSERT(size_t(i) < size_t(size()));
- *(reinterpret_cast<uchar*>(d.data())+1+(i>>3)) &= ~uchar(1 << (i & 7)); }
-
-inline void QBitArray::setBit(qsizetype i, bool val)
-{ if (val) setBit(i); else clearBit(i); }
-
-inline bool QBitArray::toggleBit(qsizetype i)
-{ Q_ASSERT(size_t(i) < size_t(size()));
- uchar b = uchar(1<<(i&7)); uchar* p = reinterpret_cast<uchar*>(d.data())+1+(i>>3);
- uchar c = uchar(*p&b); *p^=b; return c!=0; }
-
-inline bool QBitArray::operator[](qsizetype i) const { return testBit(i); }
-inline bool QBitArray::at(qsizetype i) const { return testBit(i); }
+private:
+ friend bool comparesEqual(const QBitArray &lhs, const QBitArray &rhs) noexcept
+ {
+ return lhs.d == rhs.d;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QBitArray)
+};
-class Q_CORE_EXPORT QBitRef
+class QT6_ONLY(Q_CORE_EXPORT) QBitRef
{
private:
QBitArray &a;
@@ -119,7 +160,7 @@ public:
QBitRef &operator=(bool val) { a.setBit(i, val); return *this; }
};
-inline QBitRef QBitArray::operator[](qsizetype i)
+QBitRef QBitArray::operator[](qsizetype i)
{ Q_ASSERT(i >= 0); return QBitRef(*this, i); }
#ifndef QT_NO_DATASTREAM
diff --git a/src/corelib/tools/qcommandlineoption.cpp b/src/corelib/tools/qcommandlineoption.cpp
index f1816753e6..6b990cecf1 100644
--- a/src/corelib/tools/qcommandlineoption.cpp
+++ b/src/corelib/tools/qcommandlineoption.cpp
@@ -116,8 +116,7 @@ QCommandLineOption::QCommandLineOption(const QStringList &names)
The default value for the option is set to \a defaultValue.
In Qt versions before 5.4, this constructor was \c explicit. In Qt 5.4
- and later, it no longer is and can be used for C++11-style uniform
- initialization:
+ and later, it no longer is and can be used for uniform initialization:
\snippet code/src_corelib_tools_qcommandlineoption.cpp cxx11-init
@@ -152,8 +151,7 @@ QCommandLineOption::QCommandLineOption(const QString &name, const QString &descr
The default value for the option is set to \a defaultValue.
In Qt versions before 5.4, this constructor was \c explicit. In Qt 5.4
- and later, it no longer is and can be used for C++11-style uniform
- initialization:
+ and later, it no longer is and can be used for uniform initialization:
\snippet code/src_corelib_tools_qcommandlineoption.cpp cxx11-init-list
diff --git a/src/corelib/tools/qcommandlineparser.cpp b/src/corelib/tools/qcommandlineparser.cpp
index 633ae7562c..2880eedf77 100644
--- a/src/corelib/tools/qcommandlineparser.cpp
+++ b/src/corelib/tools/qcommandlineparser.cpp
@@ -398,7 +398,7 @@ QCommandLineOption QCommandLineParser::addVersionOption()
These options are handled automatically by QCommandLineParser.
- Remember to use \c setApplicationDescription() to set the application
+ Remember to use setApplicationDescription() to set the application
description, which will be displayed when this option is used.
Example:
@@ -508,7 +508,7 @@ QString QCommandLineParser::errorText() const
if (!d->errorText.isEmpty())
return d->errorText;
if (d->unknownOptionNames.size() == 1)
- return tr("Unknown option '%1'.").arg(d->unknownOptionNames.first());
+ return tr("Unknown option '%1'.").arg(d->unknownOptionNames.constFirst());
if (d->unknownOptionNames.size() > 1)
return tr("Unknown options: %1.").arg(d->unknownOptionNames.join(QStringLiteral(", ")));
return QString();
@@ -521,7 +521,8 @@ enum MessageType { UsageMessage, ErrorMessage };
// or we are run with redirected handles (for example, by QProcess).
static inline bool displayMessageBox()
{
- if (GetConsoleWindow())
+ if (GetConsoleWindow()
+ || qEnvironmentVariableIsSet("QT_COMMAND_LINE_PARSER_NO_GUI_MESSAGE_BOXES"))
return false;
STARTUPINFO startupInfo;
startupInfo.cb = sizeof(STARTUPINFO);
@@ -673,7 +674,6 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
needsParsing = false;
bool error = false;
- const QString doubleDashString(QStringLiteral("--"));
const QLatin1Char dashChar('-');
const QLatin1Char assignChar('=');
@@ -697,7 +697,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
if (forcePositional) {
positionalArgumentList.append(argument);
- } else if (argument.startsWith(doubleDashString)) {
+ } else if (argument.startsWith("--"_L1)) {
if (argument.size() > 2) {
QString optionName = argument.mid(2).section(assignChar, 0, 0);
if (registerFoundOption(optionName)) {
@@ -786,7 +786,7 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
Returns \c true if the option \a name was set, false otherwise.
The name provided can be any long or short name of any option that was
- added with \c addOption(). All the options names are treated as being
+ added with addOption(). All the options names are treated as being
equivalent. If the name is not recognized or that option was not present,
false is returned.
@@ -812,7 +812,7 @@ bool QCommandLineParser::isSet(const QString &name) const
an empty string if not found.
The name provided can be any long or short name of any option that was
- added with \c addOption(). All the option names are treated as being
+ added with addOption(). All the option names are treated as being
equivalent. If the name is not recognized or that option was not present, an
empty string is returned.
@@ -820,7 +820,7 @@ bool QCommandLineParser::isSet(const QString &name) const
that option is returned. If the option wasn't specified on the command line,
the default value is returned.
- An empty string is returned if the option does not take a value.
+ If the option does not take a value, a warning is printed, and an empty string is returned.
\sa values(), QCommandLineOption::setDefaultValue(), QCommandLineOption::setDefaultValues()
*/
@@ -841,7 +841,7 @@ QString QCommandLineParser::value(const QString &optionName) const
optionName, or an empty list if not found.
The name provided can be any long or short name of any option that was
- added with \c addOption(). All the options names are treated as being
+ added with addOption(). All the options names are treated as being
equivalent. If the name is not recognized or that option was not present, an
empty list is returned.
@@ -861,8 +861,14 @@ QStringList QCommandLineParser::values(const QString &optionName) const
if (it != d->nameHash.cend()) {
const qsizetype optionOffset = *it;
QStringList values = d->optionValuesHash.value(optionOffset);
- if (values.isEmpty())
- values = d->commandLineOptionList.at(optionOffset).defaultValues();
+ if (values.isEmpty()) {
+ const auto &option = d->commandLineOptionList.at(optionOffset);
+ if (option.valueName().isEmpty()) {
+ qWarning("QCommandLineParser: option not expecting values: \"%ls\"",
+ qUtf16Printable(optionName));
+ }
+ values = option.defaultValues();
+ }
return values;
}
@@ -949,8 +955,8 @@ QStringList QCommandLineParser::positionalArguments() const
Names may appear more than once in this list if they were encountered
more than once by the parser.
- Any entry in the list can be used with \c value() or with
- \c values() to get any relevant option values.
+ Any entry in the list can be used with value() or with
+ values() to get any relevant option values.
*/
QStringList QCommandLineParser::optionNames() const
@@ -1100,7 +1106,8 @@ QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const
QString text;
QString usage;
// executable name
- usage += qApp ? QCoreApplication::arguments().constFirst() : QStringLiteral("<executable_name>");
+ usage += qApp ? QStringView(QCoreApplication::arguments().constFirst())
+ : QStringView(u"<executable_name>");
QList<QCommandLineOption> options = commandLineOptionList;
if (includeQtOptions && qApp)
qApp->d_func()->addQtOptions(&options);
diff --git a/src/corelib/tools/qcontainerfwd.h b/src/corelib/tools/qcontainerfwd.h
index 013ba9eab0..d5590553fa 100644
--- a/src/corelib/tools/qcontainerfwd.h
+++ b/src/corelib/tools/qcontainerfwd.h
@@ -12,7 +12,9 @@
#endif
// std headers can unfortunately not be forward declared
+#include <cstddef> // std::size_t
#include <utility>
+#include <limits>
QT_BEGIN_NAMESPACE
@@ -21,12 +23,16 @@ template <typename Key, typename T> class QHash;
template <typename Key, typename T> class QMap;
template <typename Key, typename T> class QMultiHash;
template <typename Key, typename T> class QMultiMap;
+#ifndef QT_NO_QPAIR
template <typename T1, typename T2>
using QPair = std::pair<T1, T2>;
+#endif
template <typename T> class QQueue;
template <typename T> class QSet;
+template <typename T, std::size_t E = std::size_t(-1) /* = std::dynamic_extent*/> class QSpan;
template <typename T> class QStack;
-template <typename T, qsizetype Prealloc = 256> class QVarLengthArray;
+constexpr qsizetype QVarLengthArrayDefaultPrealloc = 256;
+template <typename T, qsizetype Prealloc = QVarLengthArrayDefaultPrealloc> class QVarLengthArray;
template <typename T> class QList;
class QString;
#ifndef Q_QDOC
@@ -45,7 +51,13 @@ class QVariant;
using QVariantList = QList<QVariant>;
using QVariantMap = QMap<QString, QVariant>;
using QVariantHash = QHash<QString, QVariant>;
-using QVariantPair = QPair<QVariant, QVariant>;
+using QVariantPair = std::pair<QVariant, QVariant>;
+
+namespace QtPrivate
+{
+[[maybe_unused]]
+constexpr qsizetype MaxAllocSize = (std::numeric_limits<qsizetype>::max)();
+}
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h
index 40ed0a84e9..998bc292d4 100644
--- a/src/corelib/tools/qcontainertools_impl.h
+++ b/src/corelib/tools/qcontainertools_impl.h
@@ -72,6 +72,8 @@ template <typename T, typename N>
void q_uninitialized_relocate_n(T* first, N n, T* out)
{
if constexpr (QTypeInfo<T>::isRelocatable) {
+ static_assert(std::is_copy_constructible_v<T> || std::is_move_constructible_v<T>,
+ "Refusing to relocate this non-copy/non-move-constructible type.");
if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memcpy()
std::memcpy(static_cast<void *>(out),
static_cast<const void *>(first),
@@ -252,6 +254,13 @@ void q_relocate_overlap_n(T *first, N n, T *d_first)
}
}
+template <typename T>
+struct ArrowProxy
+{
+ T t;
+ T *operator->() noexcept { return &t; }
+};
+
template <typename Iterator>
using IfIsInputIterator = typename std::enable_if<
std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::input_iterator_tag>::value,
@@ -300,7 +309,11 @@ using IfAssociativeIteratorHasKeyAndValue =
template <typename Iterator>
using IfAssociativeIteratorHasFirstAndSecond =
- std::enable_if_t<qxp::is_detected_v<FirstAndSecondTest, Iterator>, bool>;
+ std::enable_if_t<
+ std::conjunction_v<
+ std::negation<qxp::is_detected<KeyAndValueTest, Iterator>>,
+ qxp::is_detected<FirstAndSecondTest, Iterator>
+ >, bool>;
template <typename Iterator>
using MoveBackwardsTest = decltype(
@@ -367,8 +380,7 @@ template <typename Container, typename T>
auto sequential_erase_with_copy(Container &c, const T &t)
{
using CopyProxy = std::conditional_t<std::is_copy_constructible_v<T>, T, const T &>;
- const T &tCopy = CopyProxy(t);
- return sequential_erase(c, tCopy);
+ return sequential_erase(c, CopyProxy(t));
}
template <typename Container, typename T>
diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h
index c01dbb9390..e3f98c30de 100644
--- a/src/corelib/tools/qcontiguouscache.h
+++ b/src/corelib/tools/qcontiguouscache.h
@@ -8,6 +8,7 @@
#include <QtCore/qassert.h>
#include <QtCore/qtclasshelpermacros.h>
#include <QtCore/qtcoreexports.h>
+#include <QtCore/qttypetraits.h>
#include <QtCore/qtypeinfo.h>
#include <climits>
diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp
index 2c4a2b276b..d0ed17eba2 100644
--- a/src/corelib/tools/qcryptographichash.cpp
+++ b/src/corelib/tools/qcryptographichash.cpp
@@ -25,12 +25,13 @@
#include "../../3rdparty/rfc6234/sha.h"
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
-#if !QT_CONFIG(opensslv30) || !QT_CONFIG(openssl_linked)
+#if !QT_CONFIG(openssl_hash)
// qdoc and qmake only need SHA-1
#include "../../3rdparty/md5/md5.h"
#include "../../3rdparty/md5/md5.cpp"
#include "../../3rdparty/md4/md4.h"
#include "../../3rdparty/md4/md4.cpp"
+#endif // !QT_CONFIG(openssl_hash)
typedef unsigned char BitSequence;
typedef unsigned long long DataLength;
@@ -71,6 +72,7 @@ Q_CONSTINIT static SHA3Final * const sha3Final = Final;
#endif
+#if !QT_CONFIG(openssl_hash)
/*
These 2 functions replace macros of the same name in sha224-256.c and
sha384-512.c. Originally, these macros relied on a global static 'addTemp'
@@ -114,7 +116,7 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt
#endif
#endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1
-#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(opensslv30) && QT_CONFIG(openssl_linked)
+#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(openssl_hash)
#define USING_OPENSSL30
#include <openssl/evp.h>
#include <openssl/provider.h>
@@ -266,10 +268,6 @@ static constexpr const char * methodToName(QCryptographicHash::Algorithm method)
CASE(RealSha3_256, "SHA3-256");
CASE(RealSha3_384, "SHA3-384");
CASE(RealSha3_512, "SHA3-512");
- CASE(Keccak_224, "SHA3-224");
- CASE(Keccak_256, "SHA3-256");
- CASE(Keccak_384, "SHA3-384");
- CASE(Keccak_512, "SHA3-512");
CASE(Blake2b_512, "BLAKE2B512");
CASE(Blake2s_256, "BLAKE2S256");
#undef CASE
@@ -283,7 +281,9 @@ static constexpr const char * methodToName(QCryptographicHash::Algorithm method)
*/
static constexpr bool useNonOpenSSLFallback(QCryptographicHash::Algorithm method) noexcept
{
- if (method == QCryptographicHash::Blake2b_160 || method == QCryptographicHash::Blake2b_256 ||
+ if (method == QCryptographicHash::Keccak_224 || method == QCryptographicHash::Keccak_256 ||
+ method == QCryptographicHash::Keccak_384 || method == QCryptographicHash::Keccak_512 ||
+ method == QCryptographicHash::Blake2b_160 || method == QCryptographicHash::Blake2b_256 ||
method == QCryptographicHash::Blake2b_384 || method == QCryptographicHash::Blake2s_128 ||
method == QCryptographicHash::Blake2s_160 || method == QCryptographicHash::Blake2s_224)
return true;
@@ -326,11 +326,20 @@ public:
EVP_MD_free(md);
}
};
+ struct OSSL_PROVIDER_deleter {
+ void operator()(OSSL_PROVIDER *provider) const noexcept {
+ OSSL_PROVIDER_unload(provider);
+ }
+ };
+
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, EVP_MD_CTX_deleter>;
using EVP_MD_ptr = std::unique_ptr<EVP_MD, EVP_MD_deleter>;
+ using OSSL_PROVIDER_ptr = std::unique_ptr<OSSL_PROVIDER, OSSL_PROVIDER_deleter>;
struct EVP {
EVP_MD_ptr algorithm;
EVP_MD_CTX_ptr context;
+ OSSL_PROVIDER_ptr defaultProvider;
+ OSSL_PROVIDER_ptr legacyProvider;
bool initializationFailed;
explicit EVP(QCryptographicHash::Algorithm method);
@@ -361,11 +370,11 @@ public:
SHA256Context sha256Context;
SHA384Context sha384Context;
SHA512Context sha512Context;
+#endif
SHA3Context sha3Context;
enum class Sha3Variant { Sha3, Keccak };
void sha3Finish(HashResult &result, int bitCount, Sha3Variant sha3Variant);
-#endif
blake2b_state blake2bContext;
blake2s_state blake2sContext;
#endif
@@ -378,7 +387,6 @@ public:
};
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
-#ifndef USING_OPENSSL30
void QCryptographicHashPrivate::State::sha3Finish(HashResult &result, int bitCount,
Sha3Variant sha3Variant)
{
@@ -418,7 +426,6 @@ void QCryptographicHashPrivate::State::sha3Finish(HashResult &result, int bitCou
sha3Final(&copy, result.data());
}
-#endif // !QT_CONFIG(opensslv30)
#endif
/*!
@@ -548,9 +555,15 @@ QCryptographicHash::Algorithm QCryptographicHash::algorithm() const noexcept
QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method)
{
- if (method == QCryptographicHash::Blake2b_160 ||
- method == QCryptographicHash::Blake2b_256 ||
- method == QCryptographicHash::Blake2b_384) {
+ if (method == QCryptographicHash::Keccak_224 ||
+ method == QCryptographicHash::Keccak_256 ||
+ method == QCryptographicHash::Keccak_384 ||
+ method == QCryptographicHash::Keccak_512) {
+ new (&sha3Context) SHA3Context;
+ reset(method);
+ } else if (method == QCryptographicHash::Blake2b_160 ||
+ method == QCryptographicHash::Blake2b_256 ||
+ method == QCryptographicHash::Blake2b_384) {
new (&blake2bContext) blake2b_state;
reset(method);
} else if (method == QCryptographicHash::Blake2s_128 ||
@@ -565,7 +578,11 @@ QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method)
void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm method)
{
- if (method != QCryptographicHash::Blake2b_160 &&
+ if (method != QCryptographicHash::Keccak_224 &&
+ method != QCryptographicHash::Keccak_256 &&
+ method != QCryptographicHash::Keccak_384 &&
+ method != QCryptographicHash::Keccak_512 &&
+ method != QCryptographicHash::Blake2b_160 &&
method != QCryptographicHash::Blake2b_256 &&
method != QCryptographicHash::Blake2b_384 &&
method != QCryptographicHash::Blake2s_128 &&
@@ -583,12 +600,16 @@ QCryptographicHashPrivate::EVP::EVP(QCryptographicHash::Algorithm method)
* We need to load the legacy provider in order to have the MD4
* algorithm available.
*/
- if (!OSSL_PROVIDER_load(nullptr, "legacy"))
- return;
- if (!OSSL_PROVIDER_load(nullptr, "default"))
+ legacyProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "legacy"));
+
+ if (!legacyProvider)
return;
}
+ defaultProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "default"));
+ if (!defaultProvider)
+ return;
+
context = EVP_MD_CTX_ptr(EVP_MD_CTX_new());
if (!context) {
@@ -685,9 +706,14 @@ void QCryptographicHashPrivate::reset() noexcept
void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept
{
- if (method == QCryptographicHash::Blake2b_160 ||
- method == QCryptographicHash::Blake2b_256 ||
- method == QCryptographicHash::Blake2b_384) {
+ if (method == QCryptographicHash::Keccak_224 ||
+ method == QCryptographicHash::Keccak_256 ||
+ method == QCryptographicHash::Keccak_384 ||
+ method == QCryptographicHash::Keccak_512) {
+ sha3Init(&sha3Context, hashLengthInternal(method) * 8);
+ } else if (method == QCryptographicHash::Blake2b_160 ||
+ method == QCryptographicHash::Blake2b_256 ||
+ method == QCryptographicHash::Blake2b_384) {
blake2b_init(&blake2bContext, hashLengthInternal(method));
} else if (method == QCryptographicHash::Blake2s_128 ||
method == QCryptographicHash::Blake2s_160 ||
@@ -813,9 +839,14 @@ void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm met
auto length = bytes.size();
// all functions take size_t length, so we don't need to loop around them:
{
- if (method == QCryptographicHash::Blake2b_160 ||
- method == QCryptographicHash::Blake2b_256 ||
- method == QCryptographicHash::Blake2b_384) {
+ if (method == QCryptographicHash::Keccak_224 ||
+ method == QCryptographicHash::Keccak_256 ||
+ method == QCryptographicHash::Keccak_384 ||
+ method == QCryptographicHash::Keccak_512) {
+ sha3Update(&sha3Context, reinterpret_cast<const BitSequence *>(data), uint64_t(length) * 8);
+ } else if (method == QCryptographicHash::Blake2b_160 ||
+ method == QCryptographicHash::Blake2b_256 ||
+ method == QCryptographicHash::Blake2b_384) {
blake2b_update(&blake2bContext, reinterpret_cast<const uint8_t *>(data), length);
} else if (method == QCryptographicHash::Blake2s_128 ||
method == QCryptographicHash::Blake2s_160 ||
@@ -987,9 +1018,14 @@ void QCryptographicHashPrivate::finalizeUnchecked() noexcept
void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method,
HashResult &result) noexcept
{
- if (method == QCryptographicHash::Blake2b_160 ||
- method == QCryptographicHash::Blake2b_256 ||
- method == QCryptographicHash::Blake2b_384) {
+ if (method == QCryptographicHash::Keccak_224 ||
+ method == QCryptographicHash::Keccak_256 ||
+ method == QCryptographicHash::Keccak_384 ||
+ method == QCryptographicHash::Keccak_512) {
+ sha3Finish(result, 8 * hashLengthInternal(method), Sha3Variant::Keccak);
+ } else if (method == QCryptographicHash::Blake2b_160 ||
+ method == QCryptographicHash::Blake2b_256 ||
+ method == QCryptographicHash::Blake2b_384) {
const auto length = hashLengthInternal(method);
blake2b_state copy = blake2bContext;
result.resizeForOverwrite(length);
@@ -1117,13 +1153,49 @@ void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Alg
\note In Qt versions prior to 6.3, this function took QByteArray,
not QByteArrayView.
+
+ \sa hashInto()
*/
QByteArray QCryptographicHash::hash(QByteArrayView data, Algorithm method)
{
+ QByteArray ba(hashLengthInternal(method), Qt::Uninitialized);
+ [[maybe_unused]] const auto r = hashInto(ba, data, method);
+ Q_ASSERT(r.size() == ba.size());
+ return ba;
+}
+
+/*!
+ \since 6.8
+ \fn QCryptographicHash::hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> data, Algorithm method);
+ \fn QCryptographicHash::hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> data, Algorithm method);
+ \fn QCryptographicHash::hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> data, Algorithm method);
+ \fn QCryptographicHash::hashInto(QSpan<char> buffer, QByteArrayView data, Algorithm method);
+ \fn QCryptographicHash::hashInto(QSpan<uchar> buffer, QByteArrayView data, Algorithm method);
+ \fn QCryptographicHash::hashInto(QSpan<std::byte> buffer, QByteArrayView data, Algorithm method);
+
+ Returns the hash of \a data using \a method, using \a buffer to store the result.
+
+ If \a data is a span, adds all the byte array views to the hash, in the order given.
+
+ The return value will be a sub-span of \a buffer, unless \a buffer is of
+ insufficient size, in which case a null QByteArrayView is returned.
+
+ \sa hash()
+*/
+QByteArrayView QCryptographicHash::hashInto(QSpan<std::byte> buffer,
+ QSpan<const QByteArrayView> data,
+ Algorithm method) noexcept
+{
QCryptographicHashPrivate hash(method);
- hash.addData(data);
+ for (QByteArrayView part : data)
+ hash.addData(part);
hash.finalizeUnchecked(); // no mutex needed: no-one but us has access to 'hash'
- return hash.resultView().toByteArray();
+ auto result = hash.resultView();
+ if (buffer.size() < result.size())
+ return {}; // buffer too small
+ // ### optimize: have the method directly write into `buffer`
+ memcpy(buffer.data(), result.data(), result.size());
+ return buffer.first(result.size());
}
/*!
@@ -1163,8 +1235,8 @@ bool QCryptographicHashPrivate::supportsAlgorithm(QCryptographicHash::Algorithm
if (useNonOpenSSLFallback(method))
return true;
- OSSL_PROVIDER_load(nullptr, "legacy");
- OSSL_PROVIDER_load(nullptr, "default");
+ auto legacyProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "legacy"));
+ auto defaultProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "default"));
const char *restriction = "-fips";
EVP_MD_ptr algorithm = EVP_MD_ptr(EVP_MD_fetch(nullptr, methodToName(method), restriction));
@@ -1246,7 +1318,7 @@ static constexpr int qt_hash_block_size(QCryptographicHash::Algorithm method)
return BLAKE2S_BLOCKBYTES;
#endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1
case QCryptographicHash::NumAlgorithms:
-#if !defined(Q_GCC_ONLY) || Q_CC_GCC >= 900
+#if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 900
// GCC 8 has trouble with Q_UNREACHABLE() in constexpr functions
Q_UNREACHABLE();
#endif
@@ -1289,9 +1361,9 @@ using HashBlock = QSmallByteArray<maxHashBlockSize()>;
static HashBlock xored(const HashBlock &block, quint8 val) noexcept
{
// some hints for the optimizer:
- Q_ASSUME(block.size() >= minHashBlockSize());
- Q_ASSUME(block.size() <= maxHashBlockSize());
- Q_ASSUME(block.size() % gcdHashBlockSize() == 0);
+ Q_ASSERT(block.size() >= minHashBlockSize());
+ Q_ASSERT(block.size() <= maxHashBlockSize());
+ Q_ASSERT(block.size() % gcdHashBlockSize() == 0);
HashBlock result;
result.resizeForOverwrite(block.size());
for (qsizetype i = 0; i < block.size(); ++i)
@@ -1302,7 +1374,7 @@ static HashBlock xored(const HashBlock &block, quint8 val) noexcept
class QMessageAuthenticationCodePrivate
{
public:
- QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m)
+ explicit QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m) noexcept
: messageHash(m)
{
}
@@ -1381,21 +1453,32 @@ void QMessageAuthenticationCodePrivate::initMessageHash() noexcept
\ingroup tools
\reentrant
- QMessageAuthenticationCode supports all cryptographic hashes which are supported by
- QCryptographicHash.
+ Use the QMessageAuthenticationCode class to generate hash-based message
+ authentication codes (HMACs). The class supports all cryptographic
+ hash algorithms from \l QCryptographicHash (see also
+ \l{QCryptographicHash::Algorithm}).
+
+ To generate a message authentication code, pass a suitable hash
+ algorithm and secret key to the constructor. Then process the message
+ data by calling \l addData() one or more times. After the full
+ message has been processed, get the final authentication code
+ via the \l result() function:
- To generate message authentication code, pass hash algorithm QCryptographicHash::Algorithm
- to constructor, then set key and message by setKey() and addData() functions. Result
- can be acquired by result() function.
\snippet qmessageauthenticationcode/main.cpp 0
\dots
\snippet qmessageauthenticationcode/main.cpp 1
- Alternatively, this effect can be achieved by providing message,
- key and method to hash() method.
+ For simple cases like above, you can also use the static
+ \l hash() function:
+
\snippet qmessageauthenticationcode/main.cpp 2
- \sa QCryptographicHash
+
+ \note The cryptographic strength of the HMAC depends upon the
+ size of the secret key, and the security of the
+ underlying hash function.
+
+ \sa QCryptographicHash, QCryptographicHash::Algorithm
*/
/*!
@@ -1460,7 +1543,7 @@ QMessageAuthenticationCode::~QMessageAuthenticationCode()
*/
/*!
- Resets message data. Calling this method doesn't affect the key.
+ Resets message data. Calling this function doesn't affect the key.
*/
void QMessageAuthenticationCode::reset() noexcept
{
@@ -1469,9 +1552,9 @@ void QMessageAuthenticationCode::reset() noexcept
}
/*!
- Sets secret \a key. Calling this method automatically resets the object state.
+ Sets secret \a key. Calling this function automatically resets the object state.
- For optimal performance, call this method only to \e change the active key,
+ For optimal performance, call this function only to \e change the active key,
not to set an \e initial key, as in
\code
@@ -1588,15 +1671,52 @@ void QMessageAuthenticationCodePrivate::finalizeUnchecked() noexcept
the key \a key and the method \a method.
\include qcryptographichash.cpp {qba-to-qbav-6.6}
+
+ \sa hashInto()
*/
QByteArray QMessageAuthenticationCode::hash(QByteArrayView message, QByteArrayView key,
QCryptographicHash::Algorithm method)
{
+ QByteArray ba(hashLengthInternal(method), Qt::Uninitialized);
+ [[maybe_unused]] const auto r = hashInto(ba, message, key, method);
+ Q_ASSERT(r.size() == ba.size());
+ return ba;
+}
+
+/*!
+ \since 6.8
+ \fn QMessageAuthenticationCode::hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method);
+ \fn QMessageAuthenticationCode::hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method);
+ \fn QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method);
+ \fn QMessageAuthenticationCode::hashInto(QSpan<char> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method);
+ \fn QMessageAuthenticationCode::hashInto(QSpan<uchar> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method);
+ \fn QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method);
+
+ Returns the authentication code for the message (\a message or, for the
+ QSpan overloads, the concatenation of \a messageParts) using the key \a key
+ and the method \a method.
+
+ The return value will be a sub-span of \a buffer, unless \a buffer is of
+ insufficient size, in which case a null QByteArrayView is returned.
+
+ \sa hash()
+*/
+QByteArrayView QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer,
+ QSpan<const QByteArrayView> messageParts,
+ QByteArrayView key,
+ QCryptographicHash::Algorithm method) noexcept
+{
QMessageAuthenticationCodePrivate mac(method);
mac.setKey(key);
- mac.messageHash.addData(message);
+ for (QByteArrayView part : messageParts)
+ mac.messageHash.addData(part);
mac.finalizeUnchecked();
- return mac.messageHash.resultView().toByteArray();
+ auto result = mac.messageHash.resultView();
+ if (buffer.size() < result.size())
+ return {}; // buffer too small
+ // ### optimize: have the method directly write into `buffer`
+ memcpy(buffer.data(), result.data(), result.size());
+ return buffer.first(result.size());
}
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qcryptographichash.h b/src/corelib/tools/qcryptographichash.h
index 294453adce..8c4f70457f 100644
--- a/src/corelib/tools/qcryptographichash.h
+++ b/src/corelib/tools/qcryptographichash.h
@@ -8,6 +8,7 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qobjectdefs.h>
+#include <QtCore/qspan.h>
QT_BEGIN_NAMESPACE
@@ -91,6 +92,19 @@ public:
static QByteArray hash(const QByteArray &data, Algorithm method);
#endif
static QByteArray hash(QByteArrayView data, Algorithm method);
+
+ static QByteArrayView hashInto(QSpan<char> buffer, QByteArrayView data, Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), {&data, 1}, method); }
+ static QByteArrayView hashInto(QSpan<uchar> buffer, QByteArrayView data, Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), {&data, 1}, method); }
+ static QByteArrayView hashInto(QSpan<std::byte> buffer, QByteArrayView data, Algorithm method) noexcept
+ { return hashInto(buffer, {&data, 1}, method); }
+ static QByteArrayView hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> data, Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), data, method); }
+ static QByteArrayView hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> data, Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), data, method); }
+ static QByteArrayView hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> data, Algorithm method) noexcept;
+
static int hashLength(Algorithm method);
static bool supportsAlgorithm(Algorithm method);
private:
diff --git a/src/corelib/tools/qduplicatetracker_p.h b/src/corelib/tools/qduplicatetracker_p.h
index 950220184f..23465ecffe 100644
--- a/src/corelib/tools/qduplicatetracker_p.h
+++ b/src/corelib/tools/qduplicatetracker_p.h
@@ -16,7 +16,7 @@
#include <private/qglobal_p.h>
-#if __has_include(<memory_resource>)
+#ifdef __cpp_lib_memory_resource
# include <unordered_set>
# include <memory_resource>
# include <qhash.h> // for the hashing helpers
diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp
index d8b3367de3..52602a0256 100644
--- a/src/corelib/tools/qeasingcurve.cpp
+++ b/src/corelib/tools/qeasingcurve.cpp
@@ -1153,32 +1153,37 @@ QEasingCurve::~QEasingCurve()
*/
/*!
- Compare this easing curve with \a other and returns \c true if they are
- equal. It will also compare the properties of a curve.
+ \fn bool QEasingCurve::operator==(const QEasingCurve &lhs, const QEasingCurve &rhs)
+
+ Compares easing curve \a lhs with \a rhs and returns \c true if they are
+ equal; otherwise returns \c false.
+ It will also compare the properties of the curves.
*/
-bool QEasingCurve::operator==(const QEasingCurve &other) const
+bool comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs) noexcept
{
- bool res = d_ptr->func == other.d_ptr->func
- && d_ptr->type == other.d_ptr->type;
+ bool res = lhs.d_ptr->func == rhs.d_ptr->func
+ && lhs.d_ptr->type == rhs.d_ptr->type;
if (res) {
- if (d_ptr->config && other.d_ptr->config) {
+ if (lhs.d_ptr->config && rhs.d_ptr->config) {
// catch the config content
- res = d_ptr->config->operator==(*(other.d_ptr->config));
+ res = lhs.d_ptr->config->operator==(*(rhs.d_ptr->config));
- } else if (d_ptr->config || other.d_ptr->config) {
+ } else if (lhs.d_ptr->config || rhs.d_ptr->config) {
// one one has a config object, which could contain default values
- res = qFuzzyCompare(amplitude(), other.amplitude())
- && qFuzzyCompare(period(), other.period())
- && qFuzzyCompare(overshoot(), other.overshoot());
+ res = qFuzzyCompare(lhs.amplitude(), rhs.amplitude())
+ && qFuzzyCompare(lhs.period(), rhs.period())
+ && qFuzzyCompare(lhs.overshoot(), rhs.overshoot());
}
}
return res;
}
/*!
- \fn bool QEasingCurve::operator!=(const QEasingCurve &other) const
- Compare this easing curve with \a other and returns \c true if they are not equal.
- It will also compare the properties of a curve.
+ \fn bool QEasingCurve::operator!=(const QEasingCurve &lhs, const QEasingCurve &rhs)
+
+ Compares easing curve \a lhs with \a rhs and returns \c true if they are
+ not equal; otherwise returns \c false.
+ It will also compare the properties of the curves.
\sa operator==()
*/
diff --git a/src/corelib/tools/qeasingcurve.h b/src/corelib/tools/qeasingcurve.h
index 5b112d7d7d..61e9aa247d 100644
--- a/src/corelib/tools/qeasingcurve.h
+++ b/src/corelib/tools/qeasingcurve.h
@@ -8,6 +8,7 @@
QT_REQUIRE_CONFIG(easingcurve);
+#include <QtCore/qcompare.h>
#include <QtCore/qlist.h>
#include <QtCore/qobjectdefs.h>
@@ -47,9 +48,11 @@ public:
void swap(QEasingCurve &other) noexcept { qt_ptr_swap(d_ptr, other.d_ptr); }
+#if QT_CORE_REMOVED_SINCE(6, 8)
bool operator==(const QEasingCurve &other) const;
inline bool operator!=(const QEasingCurve &other) const
{ return !(this->operator==(other)); }
+#endif
qreal amplitude() const;
void setAmplitude(qreal amplitude);
@@ -81,6 +84,11 @@ private:
friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QEasingCurve &);
friend Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QEasingCurve &);
#endif
+ friend Q_CORE_EXPORT bool
+ comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs) noexcept;
+#if !QT_CORE_REMOVED_SINCE(6, 8)
+ Q_DECLARE_EQUALITY_COMPARABLE(QEasingCurve)
+#endif
};
Q_DECLARE_SHARED(QEasingCurve)
diff --git a/src/corelib/tools/qflatmap_p.h b/src/corelib/tools/qflatmap_p.h
index 7fce7c43af..d2c0d45b79 100644
--- a/src/corelib/tools/qflatmap_p.h
+++ b/src/corelib/tools/qflatmap_p.h
@@ -1099,7 +1099,9 @@ private:
containers c;
};
-template<class Key, class T, qsizetype N = 256, class Compare = std::less<Key>>
+template <class Key, class T,
+ qsizetype N = QVarLengthArrayDefaultPrealloc,
+ class Compare = std::less<Key>>
using QVarLengthFlatMap = QFlatMap<Key, T, Compare, QVarLengthArray<Key, N>, QVarLengthArray<T, N>>;
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qfreelist.cpp b/src/corelib/tools/qfreelist.cpp
index 45bd3ba8ae..db15fac5d6 100644
--- a/src/corelib/tools/qfreelist.cpp
+++ b/src/corelib/tools/qfreelist.cpp
@@ -6,6 +6,7 @@
QT_BEGIN_NAMESPACE
// default sizes and offsets (no need to define these when customizing)
+namespace QFreeListDefaultConstantsPrivate {
enum {
Offset0 = 0x00000000,
Offset1 = 0x00008000,
@@ -17,12 +18,13 @@ enum {
Size2 = Offset3 - Offset2,
Size3 = QFreeListDefaultConstants::MaxIndex - Offset3
};
+}
Q_CONSTINIT const int QFreeListDefaultConstants::Sizes[QFreeListDefaultConstants::BlockCount] = {
- Size0,
- Size1,
- Size2,
- Size3
+ QFreeListDefaultConstantsPrivate::Size0,
+ QFreeListDefaultConstantsPrivate::Size1,
+ QFreeListDefaultConstantsPrivate::Size2,
+ QFreeListDefaultConstantsPrivate::Size3
};
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qfunctionaltools_impl.cpp b/src/corelib/tools/qfunctionaltools_impl.cpp
new file mode 100644
index 0000000000..28148c39a2
--- /dev/null
+++ b/src/corelib/tools/qfunctionaltools_impl.cpp
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 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
+
+#include <QtCore/qfunctionaltools_impl.h>
+
+// Remove this file once we have tests that implicitly test all aspects of
+// CompactStorage
+
+QT_BEGIN_NAMESPACE
+
+namespace QtPrivate {
+
+#define FOR_EACH_CVREF(op) \
+ op(&) \
+ op(const &) \
+ op(&&) \
+ op(const &&) \
+ /* end */
+
+namespace _testing {
+ struct empty {};
+ struct final final {};
+ static_assert(std::is_same_v<CompactStorage<empty>,
+ detail::StorageEmptyBaseClassOptimization<empty>>);
+ static_assert(std::is_same_v<CompactStorage<final>,
+ detail::StorageByValue<final>>);
+ static_assert(std::is_same_v<CompactStorage<int>,
+ detail::StorageByValue<int>>);
+#define CHECK1(Obj, cvref) \
+ static_assert(std::is_same_v<decltype(std::declval<CompactStorage< Obj > cvref>().object()), \
+ Obj cvref>);
+#define CHECK(cvref) \
+ CHECK1(empty, cvref) \
+ CHECK1(final, cvref) \
+ CHECK1(int, cvref) \
+ /* end */
+
+ FOR_EACH_CVREF(CHECK)
+#undef CHECK
+#undef CHECK1
+} // namespace _testing
+
+} // namespace QtPrivate
+
+#undef FOR_EACH_CVREF
+
+QT_END_NAMESPACE
diff --git a/src/corelib/tools/qfunctionaltools_impl.h b/src/corelib/tools/qfunctionaltools_impl.h
index 5878b90bd6..0942d5fe7d 100644
--- a/src/corelib/tools/qfunctionaltools_impl.h
+++ b/src/corelib/tools/qfunctionaltools_impl.h
@@ -42,6 +42,7 @@ struct StorageByValue
template <typename Object, typename Tag = void>
struct StorageEmptyBaseClassOptimization : Object
{
+ StorageEmptyBaseClassOptimization() = default;
StorageEmptyBaseClassOptimization(Object &&o)
: Object(std::move(o))
{}
diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp
index f90c351118..12e90daecf 100644
--- a/src/corelib/tools/qhash.cpp
+++ b/src/corelib/tools/qhash.cpp
@@ -49,6 +49,8 @@
QT_BEGIN_NAMESPACE
+void qt_from_latin1(char16_t *dst, const char *str, size_t size) noexcept; // qstring.cpp
+
// We assume that pointers and size_t have the same size. If that assumption should fail
// on a platform the code selecting the different methods below needs to be fixed.
static_assert(sizeof(size_t) == QT_POINTER_SIZE, "size_t and pointers have different size.");
@@ -83,6 +85,7 @@ struct HashSeedStorage
void resetSeed()
{
+#ifndef QT_BOOTSTRAPPED
if (state().state < AlreadyInitialized)
return;
@@ -90,6 +93,7 @@ struct HashSeedStorage
QRandomGenerator *generator = QRandomGenerator::system();
seeds[0].storeRelaxed(sizeof(size_t) > sizeof(quint32)
? generator->generate64() : generator->generate());
+#endif
}
void clearSeed()
@@ -281,17 +285,11 @@ static inline uint64_t murmurhash(const void *key, uint64_t len, uint64_t seed)
#endif
-#if QT_POINTER_SIZE == 8
+namespace {
// This is an inlined version of the SipHash implementation that is
// trying to avoid some memcpy's from uint64 to uint8[] and back.
-//
-
-// Use SipHash-1-2, which has similar performance characteristics as
-// stablehash() above, instead of the SipHash-2-4 default
-#define cROUNDS 1
-#define dROUNDS 2
-#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
+#define ROTL(x, b) (((x) << (b)) | ((x) >> (sizeof(x) * 8 - (b))))
#define SIPROUND \
do { \
@@ -311,8 +309,7 @@ static inline uint64_t murmurhash(const void *key, uint64_t len, uint64_t seed)
v2 = ROTL(v2, 32); \
} while (0)
-Q_NEVER_INLINE Q_DECL_HOT_FUNCTION
-static uint64_t siphash(const uint8_t *in, uint64_t inlen, uint64_t seed, uint64_t seed2)
+template <int cROUNDS = 2, int dROUNDS = 4> struct SipHash64
{
/* "somepseudorandomlygeneratedbytes" */
uint64_t v0 = 0x736f6d6570736575ULL;
@@ -320,17 +317,32 @@ static uint64_t siphash(const uint8_t *in, uint64_t inlen, uint64_t seed, uint64
uint64_t v2 = 0x6c7967656e657261ULL;
uint64_t v3 = 0x7465646279746573ULL;
uint64_t b;
- uint64_t k0 = seed;
- uint64_t k1 = seed2;
- int i;
- const uint8_t *end = in + (inlen & ~7ULL);
- const int left = inlen & 7;
+ uint64_t k0;
+ uint64_t k1;
+
+ inline SipHash64(uint64_t fulllen, uint64_t seed, uint64_t seed2);
+ inline void addBlock(const uint8_t *in, size_t inlen);
+ inline uint64_t finalize(const uint8_t *in, size_t left);
+};
+
+template <int cROUNDS, int dROUNDS>
+SipHash64<cROUNDS, dROUNDS>::SipHash64(uint64_t inlen, uint64_t seed, uint64_t seed2)
+{
b = inlen << 56;
+ k0 = seed;
+ k1 = seed2;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
+}
+template <int cROUNDS, int dROUNDS> Q_DECL_HOT_FUNCTION void
+SipHash64<cROUNDS, dROUNDS>::addBlock(const uint8_t *in, size_t inlen)
+{
+ Q_ASSERT((inlen & 7ULL) == 0);
+ int i;
+ const uint8_t *end = in + inlen;
for (; in != end; in += 8) {
uint64_t m = qFromUnaligned<uint64_t>(in);
v3 ^= m;
@@ -340,24 +352,31 @@ static uint64_t siphash(const uint8_t *in, uint64_t inlen, uint64_t seed, uint64
v0 ^= m;
}
+}
-
-#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 700
- QT_WARNING_DISABLE_GCC("-Wimplicit-fallthrough")
-#endif
+template <int cROUNDS, int dROUNDS> Q_DECL_HOT_FUNCTION uint64_t
+SipHash64<cROUNDS, dROUNDS>::finalize(const uint8_t *in, size_t left)
+{
+ int i;
switch (left) {
case 7:
b |= ((uint64_t)in[6]) << 48;
+ Q_FALLTHROUGH();
case 6:
b |= ((uint64_t)in[5]) << 40;
+ Q_FALLTHROUGH();
case 5:
b |= ((uint64_t)in[4]) << 32;
+ Q_FALLTHROUGH();
case 4:
b |= ((uint64_t)in[3]) << 24;
+ Q_FALLTHROUGH();
case 3:
b |= ((uint64_t)in[2]) << 16;
+ Q_FALLTHROUGH();
case 2:
b |= ((uint64_t)in[1]) << 8;
+ Q_FALLTHROUGH();
case 1:
b |= ((uint64_t)in[0]);
break;
@@ -380,7 +399,8 @@ static uint64_t siphash(const uint8_t *in, uint64_t inlen, uint64_t seed, uint64
b = v0 ^ v1 ^ v2 ^ v3;
return b;
}
-#else
+#undef SIPROUND
+
// This is a "SipHash" implementation adopted for 32bit platforms. It performs
// basically the same operations as the 64bit version using 4 byte at a time
// instead of 8.
@@ -391,12 +411,6 @@ static uint64_t siphash(const uint8_t *in, uint64_t inlen, uint64_t seed, uint64
//
// For the v0-v4 constants, simply use the first four bytes of the 64 bit versions.
//
-// Use SipHash-1-2, which has similar performance characteristics as
-// stablehash() above, instead of the SipHash-2-4 default
-#define cROUNDS 1
-#define dROUNDS 2
-
-#define ROTL(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b))))
#define SIPROUND \
do { \
@@ -416,8 +430,7 @@ static uint64_t siphash(const uint8_t *in, uint64_t inlen, uint64_t seed, uint64
v2 = ROTL(v2, 16); \
} while (0)
-Q_NEVER_INLINE Q_DECL_HOT_FUNCTION
-static uint siphash(const uint8_t *in, uint inlen, uint seed, uint seed2)
+template <int cROUNDS = 2, int dROUNDS = 4> struct SipHash32
{
/* "somepseudorandomlygeneratedbytes" */
uint v0 = 0x736f6d65U;
@@ -425,17 +438,32 @@ static uint siphash(const uint8_t *in, uint inlen, uint seed, uint seed2)
uint v2 = 0x6c796765U;
uint v3 = 0x74656462U;
uint b;
+ uint k0;
+ uint k1;
+
+ inline SipHash32(size_t fulllen, uint seed, uint seed2);
+ inline void addBlock(const uint8_t *in, size_t inlen);
+ inline uint finalize(const uint8_t *in, size_t left);
+};
+
+template <int cROUNDS, int dROUNDS> inline
+SipHash32<cROUNDS, dROUNDS>::SipHash32(size_t inlen, uint seed, uint seed2)
+{
uint k0 = seed;
uint k1 = seed2;
- int i;
- const uint8_t *end = in + (inlen & ~3ULL);
- const int left = inlen & 3;
b = inlen << 24;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
+}
+template <int cROUNDS, int dROUNDS> inline Q_DECL_HOT_FUNCTION void
+SipHash32<cROUNDS, dROUNDS>::addBlock(const uint8_t *in, size_t inlen)
+{
+ Q_ASSERT((inlen & 3ULL) == 0);
+ int i;
+ const uint8_t *end = in + inlen;
for (; in != end; in += 4) {
uint m = qFromUnaligned<uint>(in);
v3 ^= m;
@@ -445,15 +473,19 @@ static uint siphash(const uint8_t *in, uint inlen, uint seed, uint seed2)
v0 ^= m;
}
+}
-#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 700
- QT_WARNING_DISABLE_GCC("-Wimplicit-fallthrough")
-#endif
+template <int cROUNDS, int dROUNDS> inline Q_DECL_HOT_FUNCTION uint
+SipHash32<cROUNDS, dROUNDS>::finalize(const uint8_t *in, size_t left)
+{
+ int i;
switch (left) {
case 3:
b |= ((uint)in[2]) << 16;
+ Q_FALLTHROUGH();
case 2:
b |= ((uint)in[1]) << 8;
+ Q_FALLTHROUGH();
case 1:
b |= ((uint)in[0]);
break;
@@ -476,7 +508,70 @@ static uint siphash(const uint8_t *in, uint inlen, uint seed, uint seed2)
b = v0 ^ v1 ^ v2 ^ v3;
return b;
}
-#endif
+#undef SIPROUND
+#undef ROTL
+
+// Use SipHash-1-2, which has similar performance characteristics as
+// stablehash() above, instead of the SipHash-2-4 default
+template <int cROUNDS = 1, int dROUNDS = 2>
+using SipHash = std::conditional_t<sizeof(void *) == 8,
+ SipHash64<cROUNDS, dROUNDS>, SipHash32<cROUNDS, dROUNDS>>;
+} // unnamed namespace
+
+Q_NEVER_INLINE Q_DECL_HOT_FUNCTION
+static size_t siphash(const uint8_t *in, size_t inlen, size_t seed, size_t seed2)
+{
+ constexpr size_t TailSizeMask = sizeof(void *) - 1;
+ SipHash<> hasher(inlen, seed, seed2);
+ hasher.addBlock(in, inlen & ~TailSizeMask);
+ return hasher.finalize(in + (inlen & ~TailSizeMask), inlen & TailSizeMask);
+}
+
+enum ZeroExtension {
+ None = 0,
+ ByteToWord = 1,
+};
+
+template <ZeroExtension = None> static size_t
+qHashBits_fallback(const uchar *p, size_t size, size_t seed, size_t seed2) noexcept;
+template <> size_t qHashBits_fallback<None>(const uchar *p, size_t size, size_t seed, size_t seed2) noexcept
+{
+ if (size <= QT_POINTER_SIZE)
+ return murmurhash(p, size, seed);
+
+ return siphash(reinterpret_cast<const uchar *>(p), size, seed, seed2);
+}
+
+template <> size_t qHashBits_fallback<ByteToWord>(const uchar *data, size_t size, size_t seed, size_t seed2) noexcept
+{
+ auto quick_from_latin1 = [](char16_t *dest, const uchar *data, size_t size) {
+ // Quick, "inlined" version for very short blocks
+ std::copy_n(data, size, dest);
+ };
+ if (size <= QT_POINTER_SIZE / 2) {
+ std::array<char16_t, QT_POINTER_SIZE / 2> buf;
+ quick_from_latin1(buf.data(), data, size);
+ return murmurhash(buf.data(), size * 2, seed);
+ }
+
+ constexpr size_t TailSizeMask = sizeof(void *) / 2 - 1;
+ std::array<char16_t, 256> buf;
+ SipHash<> siphash(size * 2, seed, seed2);
+ ptrdiff_t offset = 0;
+ for ( ; offset + buf.size() < size; offset += buf.size()) {
+ qt_from_latin1(buf.data(), reinterpret_cast<const char *>(data) + offset, buf.size());
+ siphash.addBlock(reinterpret_cast<uint8_t *>(buf.data()), sizeof(buf));
+ }
+ if (size_t n = size - offset; n > TailSizeMask) {
+ n &= ~TailSizeMask;
+ qt_from_latin1(buf.data(), reinterpret_cast<const char *>(data) + offset, n);
+ siphash.addBlock(reinterpret_cast<uint8_t *>(buf.data()), n * 2);
+ offset += n;
+ }
+
+ quick_from_latin1(buf.data(), data + offset, size - offset);
+ return siphash.finalize(reinterpret_cast<uint8_t *>(buf.data()), (size - offset) * 2);
+}
#if defined(__SANITIZE_ADDRESS__) || defined(__SANITIZE_THREAD__) // GCC
# define QHASH_AES_SANITIZER_BUILD
@@ -523,10 +618,41 @@ namespace {
// the scrambling round (step 3 in [1]) because it's just very good at
// spreading the bits around.
//
+ // Note on Latin-1 hashing (ZX == ByteToWord): for simplicity of the
+ // algorithm, we pass sizes equivalent to the UTF-16 content (ZX == None).
+ // That means we must multiply by 2 on entry, divide by 2 on pointer
+ // advancing, and load half as much data from memory (though we produce
+ // exactly as much data in registers). The compilers appear to optimize
+ // this out.
+ //
// [1] https://en.wikipedia.org/wiki/Advanced_Encryption_Standard#High-level_description_of_the_algorithm
+ template <ZeroExtension ZX, typename T> static const T *advance(const T *ptr, ptrdiff_t n)
+ {
+ if constexpr (ZX == None)
+ return ptr + n;
+
+ // see note above on ZX == ByteToWord hashing
+ auto p = reinterpret_cast<const uchar *>(ptr);
+ n *= sizeof(T);
+ return reinterpret_cast<const T *>(p + n/2);
+ }
+
+ template <ZeroExtension> static __m128i loadu128(const void *ptr);
+ template <> Q_ALWAYS_INLINE QT_FUNCTION_TARGET(AES) __m128i loadu128<None>(const void *ptr)
+ {
+ return _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
+ }
+ template <> Q_ALWAYS_INLINE QT_FUNCTION_TARGET(AES) __m128i loadu128<ByteToWord>(const void *ptr)
+ {
+ // use a MOVQ followed by PMOVZXBW
+ // the compiler usually combines them as a single, loading PMOVZXBW
+ __m128i data = _mm_loadl_epi64(static_cast<const __m128i *>(ptr));
+ return _mm_cvtepu8_epi16(data);
+ }
+
// hash 16 bytes, running 3 scramble rounds of AES on itself (like label "final1")
- static void QT_FUNCTION_TARGET(AES) QT_VECTORCALL
+ static void Q_ALWAYS_INLINE QT_FUNCTION_TARGET(AES) QT_VECTORCALL
hash16bytes(__m128i &state0, __m128i data)
{
state0 = _mm_xor_si128(state0, data);
@@ -536,11 +662,12 @@ namespace {
}
// hash twice 16 bytes, running 2 scramble rounds of AES on itself
+ template <ZeroExtension ZX>
static void QT_FUNCTION_TARGET(AES) QT_VECTORCALL
hash2x16bytes(__m128i &state0, __m128i &state1, const __m128i *src0, const __m128i *src1)
{
- __m128i data0 = _mm_loadu_si128(src0);
- __m128i data1 = _mm_loadu_si128(src1);
+ __m128i data0 = loadu128<ZX>(src0);
+ __m128i data1 = loadu128<ZX>(src1);
state0 = _mm_xor_si128(data0, state0);
state1 = _mm_xor_si128(data1, state1);
state0 = _mm_aesenc_si128(state0, state0);
@@ -587,16 +714,18 @@ Q_ALWAYS_INLINE __m128i AESHashSeed::state1() const
}
}
+template <ZeroExtension ZX>
static size_t QT_FUNCTION_TARGET(AES) QT_VECTORCALL
aeshash128_16to32(__m128i state0, __m128i state1, const __m128i *src, const __m128i *srcend)
{
{
- if (src + 1 < srcend) {
+ const __m128i *src2 = advance<ZX>(srcend, -1);
+ if (advance<ZX>(src, 1) < srcend) {
// epilogue: between 16 and 31 bytes
- hash2x16bytes(state0, state1, src, srcend - 1);
+ hash2x16bytes<ZX>(state0, state1, src, src2);
} else if (src != srcend) {
// epilogue: between 1 and 16 bytes, overlap with the end
- __m128i data = _mm_loadu_si128(srcend - 1);
+ __m128i data = loadu128<ZX>(src2);
hash16bytes(state0, data);
}
@@ -607,8 +736,21 @@ aeshash128_16to32(__m128i state0, __m128i state1, const __m128i *src, const __m1
return mm_cvtsi128_sz(state0);
}
+// load all 16 bytes and mask off the bytes past the end of the source
+static const qint8 maskarray[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+// load 16 bytes ending at the data end, then shuffle them to the beginning
+static const qint8 shufflecontrol[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+template <ZeroExtension ZX>
static size_t QT_FUNCTION_TARGET(AES) QT_VECTORCALL
-aeshash128_lt16(__m128i state0, const uchar *p, size_t len)
+aeshash128_lt16(__m128i state0, const __m128i *src, const __m128i *srcend, size_t len)
{
if (len) {
// We're going to load 16 bytes and mask zero the part we don't care
@@ -616,28 +758,18 @@ aeshash128_lt16(__m128i state0, const uchar *p, size_t len)
// including NULLs at the end because the length is in the key)
// WARNING: this may produce valgrind warnings, but it's safe
- constexpr quintptr PageSize = 4096;
+ constexpr quintptr CachelineSize = 64;
__m128i data;
- if ((quintptr(p) & (PageSize / 2)) == 0) {
- // lower half of the page:
- // load all 16 bytes and mask off the bytes past the end of the source
- static const qint8 maskarray[] = {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- };
+ if ((quintptr(src) & (CachelineSize / 2)) == 0) {
+ // lower half of the cacheline:
__m128i mask = _mm_loadu_si128(reinterpret_cast<const __m128i *>(maskarray + 15 - len));
- data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p));
+ data = loadu128<ZX>(src);
data = _mm_and_si128(data, mask);
} else {
- // upper half of the page:
- // load 16 bytes ending at the data end, then shuffle them to the beginning
- static const qint8 shufflecontrol[] = {
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
- };
+ // upper half of the cacheline:
__m128i control = _mm_loadu_si128(reinterpret_cast<const __m128i *>(shufflecontrol + 15 - len));
- data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(p + len) - 1);
+ data = loadu128<ZX>(advance<ZX>(srcend, -1));
data = _mm_shuffle_epi8(data, control);
}
@@ -646,24 +778,45 @@ aeshash128_lt16(__m128i state0, const uchar *p, size_t len)
return mm_cvtsi128_sz(state0);
}
+template <ZeroExtension ZX>
static size_t QT_FUNCTION_TARGET(AES) QT_VECTORCALL
aeshash128_ge32(__m128i state0, __m128i state1, const __m128i *src, const __m128i *srcend)
{
// main loop: scramble two 16-byte blocks
- for ( ; src + 2 < srcend; src += 2)
- hash2x16bytes(state0, state1, src, src + 1);
+ for ( ; advance<ZX>(src, 2) < srcend; src = advance<ZX>(src, 2))
+ hash2x16bytes<ZX>(state0, state1, src, advance<ZX>(src, 1));
- return aeshash128_16to32(state0, state1, src, srcend);
+ return aeshash128_16to32<ZX>(state0, state1, src, srcend);
}
# if QT_COMPILER_SUPPORTS_HERE(VAES)
-static size_t QT_FUNCTION_TARGET(ARCH_ICL) QT_VECTORCALL
+template <ZeroExtension> static __m256i loadu256(const void *ptr);
+template <> Q_ALWAYS_INLINE QT_FUNCTION_TARGET(VAES) __m256i loadu256<None>(const void *ptr)
+{
+ return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr));
+}
+template <> Q_ALWAYS_INLINE QT_FUNCTION_TARGET(VAES) __m256i loadu256<ByteToWord>(const void *ptr)
+{
+ // VPMOVZXBW xmm, ymm
+ __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i *>(ptr));
+ return _mm256_cvtepu8_epi16(data);
+}
+
+template <ZeroExtension ZX>
+static size_t QT_FUNCTION_TARGET(VAES_AVX512) QT_VECTORCALL
aeshash256_lt32_avx256(__m256i state0, const uchar *p, size_t len)
{
__m128i state0_128 = _mm256_castsi256_si128(state0);
if (len) {
- __mmask32 mask = _bzhi_u32(-1, unsigned(len));
- __m256i data = _mm256_maskz_loadu_epi8(mask, p);
+ __m256i data;
+ if constexpr (ZX == None) {
+ __mmask32 mask = _bzhi_u32(-1, unsigned(len));
+ data = _mm256_maskz_loadu_epi8(mask, p);
+ } else {
+ __mmask16 mask = _bzhi_u32(-1, unsigned(len) / 2);
+ __m128i data0 = _mm_maskz_loadu_epi8(mask, p);
+ data = _mm256_cvtepu8_epi16(data0);
+ }
__m128i data0 = _mm256_castsi256_si128(data);
if (len >= sizeof(__m128i)) {
state0 = _mm256_xor_si256(state0, data);
@@ -683,8 +836,9 @@ aeshash256_lt32_avx256(__m256i state0, const uchar *p, size_t len)
return mm_cvtsi128_sz(state0_128);
}
+template <ZeroExtension ZX>
static size_t QT_FUNCTION_TARGET(VAES) QT_VECTORCALL
-aeshash256_ge32(__m256i state0, const uchar *p, size_t len)
+aeshash256_ge32(__m256i state0, const __m128i *s, const __m128i *end, size_t len)
{
static const auto hash32bytes = [](__m256i &state0, __m256i data) QT_FUNCTION_TARGET(VAES) {
state0 = _mm256_xor_si256(state0, data);
@@ -694,10 +848,10 @@ aeshash256_ge32(__m256i state0, const uchar *p, size_t len)
};
// hash twice 32 bytes, running 2 scramble rounds of AES on itself
- const auto hash2x32bytes = [](__m256i &state0, __m256i &state1, const __m256i *src0,
- const __m256i *src1) QT_FUNCTION_TARGET(VAES) {
- __m256i data0 = _mm256_loadu_si256(src0);
- __m256i data1 = _mm256_loadu_si256(src1);
+ const auto hash2x32bytes = [](__m256i &state0, __m256i &state1, const void *src0,
+ const void *src1) QT_FUNCTION_TARGET(VAES) {
+ __m256i data0 = loadu256<ZX>(src0);
+ __m256i data1 = loadu256<ZX>(src1);
state0 = _mm256_xor_si256(data0, state0);
state1 = _mm256_xor_si256(data1, state1);
state0 = _mm256_aesenc_epi128(state0, state0);
@@ -706,21 +860,22 @@ aeshash256_ge32(__m256i state0, const uchar *p, size_t len)
state1 = _mm256_aesenc_epi128(state1, state1);
};
- const __m256i *src = reinterpret_cast<const __m256i *>(p);
- const __m256i *srcend = reinterpret_cast<const __m256i *>(p + len);
+ const __m256i *src = reinterpret_cast<const __m256i *>(s);
+ const __m256i *srcend = reinterpret_cast<const __m256i *>(end);
__m256i state1 = _mm256_aesenc_epi128(state0, mm256_set1_epz(len));
// main loop: scramble two 32-byte blocks
- for ( ; src + 2 < srcend; src += 2)
- hash2x32bytes(state0, state1, src, src + 1);
+ for ( ; advance<ZX>(src, 2) < srcend; src = advance<ZX>(src, 2))
+ hash2x32bytes(state0, state1, src, advance<ZX>(src, 1));
- if (src + 1 < srcend) {
+ const __m256i *src2 = advance<ZX>(srcend, -1);
+ if (advance<ZX>(src, 1) < srcend) {
// epilogue: between 32 and 31 bytes
- hash2x32bytes(state0, state1, src, srcend - 1);
+ hash2x32bytes(state0, state1, src, src2);
} else if (src != srcend) {
// epilogue: between 1 and 32 bytes, overlap with the end
- __m256i data = _mm256_loadu_si256(srcend - 1);
+ __m256i data = loadu256<ZX>(src2);
hash32bytes(state0, data);
}
@@ -733,59 +888,69 @@ aeshash256_ge32(__m256i state0, const uchar *p, size_t len)
return mm_cvtsi128_sz(_mm_xor_si128(low, high));
}
+template <ZeroExtension ZX>
static size_t QT_FUNCTION_TARGET(VAES)
aeshash256(const uchar *p, size_t len, size_t seed, size_t seed2) noexcept
{
AESHashSeed state(seed, seed2);
auto src = reinterpret_cast<const __m128i *>(p);
- const auto srcend = reinterpret_cast<const __m128i *>(p + len);
+ const auto srcend = reinterpret_cast<const __m128i *>(advance<ZX>(p, len));
if (len < sizeof(__m128i))
- return aeshash128_lt16(state.state0, p, len);
+ return aeshash128_lt16<ZX>(state.state0, src, srcend, len);
if (len <= sizeof(__m256i))
- return aeshash128_16to32(state.state0, state.state1(), src, srcend);
+ return aeshash128_16to32<ZX>(state.state0, state.state1(), src, srcend);
- return aeshash256_ge32(state.state0_256(), p, len);
+ return aeshash256_ge32<ZX>(state.state0_256(), src, srcend, len);
}
+template <ZeroExtension ZX>
static size_t QT_FUNCTION_TARGET(VAES_AVX512)
aeshash256_avx256(const uchar *p, size_t len, size_t seed, size_t seed2) noexcept
{
AESHashSeed state(seed, seed2);
+ auto src = reinterpret_cast<const __m128i *>(p);
+ const auto srcend = reinterpret_cast<const __m128i *>(advance<ZX>(p, len));
+
if (len <= sizeof(__m256i))
- return aeshash256_lt32_avx256(state.state0_256(), p, len);
+ return aeshash256_lt32_avx256<ZX>(state.state0_256(), p, len);
- return aeshash256_ge32(state.state0_256(), p, len);
+ return aeshash256_ge32<ZX>(state.state0_256(), src, srcend, len);
}
# endif // VAES
+template <ZeroExtension ZX>
static size_t QT_FUNCTION_TARGET(AES)
aeshash128(const uchar *p, size_t len, size_t seed, size_t seed2) noexcept
{
AESHashSeed state(seed, seed2);
auto src = reinterpret_cast<const __m128i *>(p);
- const auto srcend = reinterpret_cast<const __m128i *>(p + len);
+ const auto srcend = reinterpret_cast<const __m128i *>(advance<ZX>(p, len));
if (len < sizeof(__m128i))
- return aeshash128_lt16(state.state0, p, len);
+ return aeshash128_lt16<ZX>(state.state0, src, srcend, len);
if (len <= sizeof(__m256i))
- return aeshash128_16to32(state.state0, state.state1(), src, srcend);
+ return aeshash128_16to32<ZX>(state.state0, state.state1(), src, srcend);
- return aeshash128_ge32(state.state0, state.state1(), src, srcend);
+ return aeshash128_ge32<ZX>(state.state0, state.state1(), src, srcend);
}
+template <ZeroExtension ZX = None>
static size_t aeshash(const uchar *p, size_t len, size_t seed, size_t seed2) noexcept
{
+ if constexpr (ZX == ByteToWord)
+ len *= 2; // see note above on ZX == ByteToWord hashing
+
# if QT_COMPILER_SUPPORTS_HERE(VAES)
if (qCpuHasFeature(VAES)) {
if (qCpuHasFeature(AVX512VL))
- return aeshash256_avx256(p, len, seed, seed2);
- return aeshash256(p, len, seed, seed2);
+ return aeshash256_avx256<ZX>(p, len, seed, seed2);
+ return aeshash256<ZX>(p, len, seed, seed2);
}
# endif
- return aeshash128(p, len, seed, seed2);
+ return aeshash128<ZX>(p, len, seed, seed2);
}
#endif // x86 AESNI
@@ -933,24 +1098,17 @@ size_t qHashBits(const void *p, size_t size, size_t seed) noexcept
size_t seed2 = size;
if (seed)
seed2 = qt_qhash_seed.currentSeed(1);
+
+ auto data = reinterpret_cast<const uchar *>(p);
#ifdef AESHASH
if (seed && qCpuHasFeature(AES) && qCpuHasFeature(SSE4_2))
- return aeshash(reinterpret_cast<const uchar *>(p), size, seed, seed2);
+ return aeshash(data, size, seed, seed2);
#elif defined(Q_PROCESSOR_ARM) && QT_COMPILER_SUPPORTS_HERE(AES) && !defined(QHASH_AES_SANITIZER_BUILD) && !defined(QT_BOOTSTRAPPED)
-# if defined(Q_OS_LINUX)
- // Do specific runtime-only check as Yocto hard enables Crypto extension for
- // all armv8 configs
- if (seed && (qCpuFeatures() & CpuFeatureAES))
-# else
if (seed && qCpuHasFeature(AES))
-# endif
- return aeshash(reinterpret_cast<const uchar *>(p), size, seed, seed2);
+ return aeshash(data, size, seed, seed2);
#endif
- if (size <= QT_POINTER_SIZE)
- return murmurhash(p, size, seed);
-
- return siphash(reinterpret_cast<const uchar *>(p), size, seed, seed2);
+ return qHashBits_fallback<>(data, size, seed, seed2);
}
size_t qHash(QByteArrayView key, size_t seed) noexcept
@@ -963,6 +1121,7 @@ size_t qHash(QStringView key, size_t seed) noexcept
return qHashBits(key.data(), key.size()*sizeof(QChar), seed);
}
+#ifndef QT_BOOTSTRAPPED
size_t qHash(const QBitArray &bitArray, size_t seed) noexcept
{
qsizetype m = bitArray.d.size() - 1;
@@ -975,10 +1134,30 @@ size_t qHash(const QBitArray &bitArray, size_t seed) noexcept
result = ((result << 4) + bitArray.d.at(m)) & ((1 << n) - 1);
return result;
}
+#endif
size_t qHash(QLatin1StringView key, size_t seed) noexcept
{
- return qHashBits(reinterpret_cast<const uchar *>(key.data()), size_t(key.size()), seed);
+#ifdef QT_BOOTSTRAPPED
+ // the seed is always 0 in bootstrapped mode (no seed generation code),
+ // so help the compiler do dead code elimination
+ seed = 0;
+#endif
+
+ auto data = reinterpret_cast<const uchar *>(key.data());
+ size_t size = key.size();
+
+ // Mix in the length as a secondary seed.
+ // Multiplied by 2 to match the byte size of the equiavlent UTF-16 string.
+ size_t seed2 = size * 2;
+ if (seed)
+ seed2 = qt_qhash_seed.currentSeed(1);
+
+#if defined(AESHASH)
+ if (seed && qCpuHasFeature(AES) && qCpuHasFeature(SSE4_2))
+ return aeshash<ByteToWord>(data, size, seed, seed2);
+#endif
+ return qHashBits_fallback<ByteToWord>(data, size, seed, seed2);
}
/*!
@@ -1383,6 +1562,26 @@ uint qt_hash(QStringView key, uint chained) noexcept
Returns the hash value for the \a key, using \a seed to seed the calculation.
*/
+/*! \fn size_t qHash(quint128 key, size_t seed = 0)
+ \relates QHash
+ \since 6.8
+
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+
+ \note This function is only available on platforms that support a native
+ 128-bit integer type.
+*/
+
+/*! \fn size_t qHash(qint128 key, size_t seed = 0)
+ \relates QHash
+ \since 6.8
+
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+
+ \note This function is only available on platforms that support a native
+ 128-bit integer type.
+ */
+
/*! \fn size_t qHash(char8_t key, size_t seed = 0)
\relates QHash
\since 6.0
@@ -1436,7 +1635,6 @@ size_t qHash(double key, size_t seed) noexcept
}
}
-#if !defined(Q_OS_DARWIN) || defined(Q_QDOC)
/*! \relates QHash
\since 5.3
@@ -1454,7 +1652,6 @@ size_t qHash(long double key, size_t seed) noexcept
return murmurhash(&key, sizeof(key), seed);
}
}
-#endif
/*! \fn size_t qHash(const QChar key, size_t seed = 0)
\relates QHash
@@ -1512,7 +1709,7 @@ size_t qHash(long double key, size_t seed) noexcept
Returns the hash value for the \a key, using \a seed to seed the calculation.
*/
-/*! \fn template <class T> size_t qHash(std::nullptr_t key, size_t seed = 0)
+/*! \fn size_t qHash(std::nullptr_t key, size_t seed = 0)
\relates QHash
\since 6.0
@@ -1783,8 +1980,8 @@ size_t qHash(long double key, size_t seed) noexcept
Constructs a hash with a copy of each of the elements in the iterator range
[\a begin, \a end). Either the elements iterated by the range must be
- objects with \c{first} and \c{second} data members (like \c{QPair},
- \c{std::pair}, etc.) convertible to \c Key and to \c T respectively; or the
+ objects with \c{first} and \c{second} data members (like \c{std::pair}),
+ convertible to \c Key and to \c T respectively; or the
iterators must have \c{key()} and \c{value()} member functions, returning a
key convertible to \c Key and a value convertible to \c T respectively.
*/
@@ -2019,7 +2216,7 @@ size_t qHash(long double key, size_t seed) noexcept
Returns \c true if the hash contains an item with the \a key;
otherwise returns \c false.
- \sa count(), QMultiHash::contains()
+ \sa count()
*/
/*! \fn template <class Key, class T> T QHash<Key, T>::value(const Key &key) const
@@ -2042,6 +2239,12 @@ size_t qHash(long double key, size_t seed) noexcept
a \l{default-constructed value} into the hash with the \a key, and
returns a reference to it.
+//! [qhash-iterator-invalidation-func-desc]
+ \warning Returned iterators/references should be considered invalidated
+ the next time you call a non-const function on the hash, or when the
+ hash is destroyed.
+//! [qhash-iterator-invalidation-func-desc]
+
\sa insert(), value()
*/
@@ -2125,12 +2328,16 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in
the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa constBegin(), end()
*/
/*! \fn template <class Key, class T> QHash<Key, T>::const_iterator QHash<Key, T>::begin() const
\overload
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*! \fn template <class Key, class T> QHash<Key, T>::const_iterator QHash<Key, T>::cbegin() const
@@ -2139,6 +2346,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa begin(), cend()
*/
@@ -2147,6 +2356,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa begin(), constEnd()
*/
@@ -2156,6 +2367,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first key
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyEnd()
*/
@@ -2164,12 +2377,16 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item
after the last item in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa begin(), constEnd()
*/
/*! \fn template <class Key, class T> QHash<Key, T>::const_iterator QHash<Key, T>::end() const
\overload
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*! \fn template <class Key, class T> QHash<Key, T>::const_iterator QHash<Key, T>::constEnd() const
@@ -2177,6 +2394,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
item after the last item in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa constBegin(), end()
*/
@@ -2186,6 +2405,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
item after the last item in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa cbegin(), end()
*/
@@ -2195,6 +2416,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
item after the last key in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyBegin()
*/
@@ -2204,6 +2427,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first entry
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueEnd()
*/
@@ -2213,6 +2438,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
entry after the last entry in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueBegin()
*/
@@ -2222,6 +2449,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first entry
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueEnd()
*/
@@ -2231,6 +2460,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first entry
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueBegin()
*/
@@ -2240,6 +2471,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
entry after the last entry in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueBegin()
*/
@@ -2249,6 +2482,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
entry after the last entry in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa constKeyValueBegin()
*/
@@ -2268,6 +2503,8 @@ size_t qHash(long double key, size_t seed) noexcept
references to the ones in the hash. Specifically, mutating the value
will modify the hash itself.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa QKeyValueIterator
*/
@@ -2285,6 +2522,8 @@ size_t qHash(long double key, size_t seed) noexcept
\snippet code/src_corelib_tools_qhash.cpp 15
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa remove(), take(), find()
*/
@@ -2304,12 +2543,16 @@ size_t qHash(long double key, size_t seed) noexcept
\snippet code/src_corelib_tools_qhash.cpp 16
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa value(), values()
*/
/*! \fn template <class Key, class T> QHash<Key, T>::const_iterator QHash<Key, T>::find(const Key &key) const
\overload
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*! \fn template <class Key, class T> QHash<Key, T>::const_iterator QHash<Key, T>::constFind(const Key &key) const
@@ -2321,6 +2564,8 @@ size_t qHash(long double key, size_t seed) noexcept
If the hash contains no item with the \a key, the function
returns constEnd().
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa find()
*/
@@ -2330,6 +2575,10 @@ size_t qHash(long double key, size_t seed) noexcept
If there is already an item with the \a key, that item's value
is replaced with \a value.
+
+ Returns an iterator pointing to the new/updated element.
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*!
@@ -2341,6 +2590,8 @@ size_t qHash(long double key, size_t seed) noexcept
construction.
Returns an iterator pointing to the new element.
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
@@ -2360,17 +2611,21 @@ size_t qHash(long double key, size_t seed) noexcept
returns \c false.
*/
-/*! \fn template <class Key, class T> QPair<iterator, iterator> QMultiHash<Key, T>::equal_range(const Key &key)
+/*! \fn template <class Key, class T> std::pair<iterator, iterator> QMultiHash<Key, T>::equal_range(const Key &key)
\since 5.7
Returns a pair of iterators delimiting the range of values \c{[first, second)}, that
are stored under \a key. If the range is empty then both iterators will be equal to end().
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*!
- \fn template <class Key, class T> QPair<const_iterator, const_iterator> QMultiHash<Key, T>::equal_range(const Key &key) const
+ \fn template <class Key, class T> std::pair<const_iterator, const_iterator> QMultiHash<Key, T>::equal_range(const Key &key) const
\overload
\since 5.7
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*! \typedef QHash::ConstIterator
@@ -2937,9 +3192,6 @@ size_t qHash(long double key, size_t seed) noexcept
Constructs a multi-hash with a copy of each of the elements in the
initializer list \a list.
-
- This function is only available if the program is being
- compiled in C++11 mode.
*/
/*! \fn template <class Key, class T> QMultiHash<Key, T>::QMultiHash(const QHash<Key, T> &other)
@@ -2953,8 +3205,8 @@ size_t qHash(long double key, size_t seed) noexcept
Constructs a multi-hash with a copy of each of the elements in the iterator range
[\a begin, \a end). Either the elements iterated by the range must be
- objects with \c{first} and \c{second} data members (like \c{QPair},
- \c{std::pair}, etc.) convertible to \c Key and to \c T respectively; or the
+ objects with \c{first} and \c{second} data members (like \c{std::pair}),
+ convertible to \c Key and to \c T respectively; or the
iterators must have \c{key()} and \c{value()} member functions, returning a
key convertible to \c Key and a value convertible to \c T respectively.
*/
@@ -2969,6 +3221,10 @@ size_t qHash(long double key, size_t seed) noexcept
If there are multiple items with the \a key, the most
recently inserted item's value is replaced with \a value.
+ Returns an iterator pointing to the new/updated element.
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa insert()
*/
@@ -2981,6 +3237,10 @@ size_t qHash(long double key, size_t seed) noexcept
different from replace(), which overwrites the value of an
existing item.)
+ Returns an iterator pointing to the new element.
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa replace()
*/
@@ -2999,6 +3259,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an iterator pointing to the new element.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa insert
*/
@@ -3015,6 +3277,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an iterator pointing to the new element.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa replace, emplace
*/
@@ -3081,6 +3345,8 @@ size_t qHash(long double key, size_t seed) noexcept
If the hash contains multiple items with the \a key, this function returns
a reference to the most recently inserted value.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa insert(), value()
*/
@@ -3233,12 +3499,16 @@ size_t qHash(long double key, size_t seed) noexcept
If the hash contains multiple items with the \a key and \a value, the
iterator returned points to the most recently inserted item.
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*!
\fn template <class Key, class T> typename QMultiHash<Key, T>::const_iterator QMultiHash<Key, T>::find(const Key &key, const T &value) const
\since 4.3
\overload
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*!
@@ -3250,6 +3520,8 @@ size_t qHash(long double key, size_t seed) noexcept
If the hash contains no such item, the function returns
constEnd().
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*! \fn template <class Key, class T> QMultiHash<Key, T>::iterator QMultiHash<Key, T>::begin()
@@ -3257,12 +3529,16 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in
the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa constBegin(), end()
*/
/*! \fn template <class Key, class T> QMultiHash<Key, T>::const_iterator QMultiHash<Key, T>::begin() const
\overload
+
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
*/
/*! \fn template <class Key, class T> QMultiHash<Key, T>::const_iterator QMultiHash<Key, T>::cbegin() const
@@ -3271,6 +3547,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa begin(), cend()
*/
@@ -3279,6 +3557,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa begin(), constEnd()
*/
@@ -3288,6 +3568,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first key
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyEnd()
*/
@@ -3296,6 +3578,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item
after the last item in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa begin(), constEnd()
*/
@@ -3309,6 +3593,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
item after the last item in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa constBegin(), end()
*/
@@ -3318,6 +3604,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
item after the last item in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa cbegin(), end()
*/
@@ -3327,6 +3615,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
item after the last key in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyBegin()
*/
@@ -3336,6 +3626,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first entry
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueEnd()
*/
@@ -3345,6 +3637,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
entry after the last entry in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueBegin()
*/
@@ -3354,6 +3648,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first entry
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueEnd()
*/
@@ -3363,6 +3659,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first entry
in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueBegin()
*/
@@ -3372,6 +3670,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
entry after the last entry in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa keyValueBegin()
*/
@@ -3381,6 +3681,8 @@ size_t qHash(long double key, size_t seed) noexcept
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary
entry after the last entry in the hash.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa constKeyValueBegin()
*/
@@ -3400,6 +3702,8 @@ size_t qHash(long double key, size_t seed) noexcept
references to the ones in the hash. Specifically, mutating the value
will modify the hash itself.
+ \include qhash.cpp qhash-iterator-invalidation-func-desc
+
\sa QKeyValueIterator
*/
diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h
index a4de3e377c..9cc6fbf30b 100644
--- a/src/corelib/tools/qhash.h
+++ b/src/corelib/tools/qhash.h
@@ -11,6 +11,7 @@
#include <QtCore/qiterator.h>
#include <QtCore/qlist.h>
#include <QtCore/qrefcount.h>
+#include <QtCore/qttypetraits.h>
#include <initializer_list>
#include <functional> // for std::hash
@@ -584,7 +585,7 @@ struct Data
numBuckets = GrowthPolicy::bucketsForCapacity(qMax(size, reserved));
spans = allocateSpans(numBuckets).spans;
size_t otherNSpans = other.numBuckets >> SpanConstants::SpanShift;
- reallocationHelper(other, otherNSpans, true);
+ reallocationHelper(other, otherNSpans, numBuckets != other.numBuckets);
}
static Data *detached(Data *d)
@@ -677,8 +678,10 @@ struct Data
return size >= (numBuckets >> 1);
}
- Bucket findBucket(const Key &key) const noexcept
+ template <typename K> Bucket findBucket(const K &key) const noexcept
{
+ static_assert(std::is_same_v<std::remove_cv_t<Key>, K> ||
+ QHashHeterogeneousSearch<std::remove_cv_t<Key>, K>::value);
Q_ASSERT(numBuckets > 0);
size_t hash = QHashPrivate::calculateHash(key, seed);
Bucket bucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
@@ -697,7 +700,7 @@ struct Data
}
}
- Node *findNode(const Key &key) const noexcept
+ template <typename K> Node *findNode(const K &key) const noexcept
{
auto bucket = findBucket(key);
if (bucket.isUnused())
@@ -711,7 +714,7 @@ struct Data
bool initialized;
};
- InsertionResult findOrInsert(const Key &key) noexcept
+ template <typename K> InsertionResult findOrInsert(const K &key) noexcept
{
Bucket it(static_cast<Span *>(nullptr), 0);
if (numBuckets > 0) {
@@ -955,6 +958,11 @@ public:
bool remove(const Key &key)
{
+ return removeImpl(key);
+ }
+private:
+ template <typename K> bool removeImpl(const K &key)
+ {
if (isEmpty()) // prevents detaching shared null
return false;
auto it = d->findBucket(key);
@@ -967,13 +975,21 @@ public:
d->erase(it);
return true;
}
+
+public:
template <typename Predicate>
qsizetype removeIf(Predicate pred)
{
return QtPrivate::associative_erase_if(*this, pred);
}
+
T take(const Key &key)
{
+ return takeImpl(key);
+ }
+private:
+ template <typename K> T takeImpl(const K &key)
+ {
if (isEmpty()) // prevents detaching shared null
return T();
auto it = d->findBucket(key);
@@ -988,6 +1004,7 @@ public:
return value;
}
+public:
bool contains(const Key &key) const noexcept
{
if (!d)
@@ -1000,74 +1017,68 @@ public:
}
private:
- const Key *keyImpl(const T &value) const noexcept
+ template <typename Fn> Key keyImpl(const T &value, Fn &&defaultFn) const noexcept
{
if (d) {
const_iterator i = begin();
while (i != end()) {
if (i.value() == value)
- return &i.key();
+ return i.key();
++i;
}
}
- return nullptr;
+ return defaultFn();
}
public:
Key key(const T &value) const noexcept
{
- if (auto *k = keyImpl(value))
- return *k;
- else
- return Key();
+ return keyImpl(value, [] { return Key(); });
}
Key key(const T &value, const Key &defaultKey) const noexcept
{
- if (auto *k = keyImpl(value))
- return *k;
- else
- return defaultKey;
+ return keyImpl(value, [&] { return defaultKey; });
}
private:
- T *valueImpl(const Key &key) const noexcept
+ template <typename K, typename Fn> T valueImpl(const K &key, Fn &&defaultValue) const noexcept
{
if (d) {
Node *n = d->findNode(key);
if (n)
- return &n->value;
+ return n->value;
}
- return nullptr;
+ return defaultValue();
}
public:
T value(const Key &key) const noexcept
{
- if (T *v = valueImpl(key))
- return *v;
- else
- return T();
+ return valueImpl(key, [] { return T(); });
}
T value(const Key &key, const T &defaultValue) const noexcept
{
- if (T *v = valueImpl(key))
- return *v;
- else
- return defaultValue;
+ return valueImpl(key, [&] { return defaultValue; });
}
T &operator[](const Key &key)
{
+ return operatorIndexImpl(key);
+ }
+private:
+ template <typename K> T &operatorIndexImpl(const K &key)
+ {
const auto copy = isDetached() ? QHash() : *this; // keep 'key' alive across the detach
detach();
auto result = d->findOrInsert(key);
Q_ASSERT(!result.it.atEnd());
if (!result.initialized)
- Node::createInPlace(result.it.node(), key, T());
+ Node::createInPlace(result.it.node(), Key(key), T());
return result.it.node()->value;
}
+public:
const T operator[](const Key &key) const noexcept
{
return value(key);
@@ -1234,28 +1245,25 @@ public:
return i;
}
- QPair<iterator, iterator> equal_range(const Key &key)
+ std::pair<iterator, iterator> equal_range(const Key &key)
{
- auto first = find(key);
- auto second = first;
- if (second != iterator())
- ++second;
- return qMakePair(first, second);
+ return equal_range_impl(*this, key);
}
-
- QPair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
+ std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
+ {
+ return equal_range_impl(*this, key);
+ }
+private:
+ template <typename Hash, typename K> static auto equal_range_impl(Hash &self, const K &key)
{
- auto first = find(key);
+ auto first = self.find(key);
auto second = first;
- if (second != iterator())
+ if (second != decltype(first){})
++second;
- return qMakePair(first, second);
+ return std::make_pair(first, second);
}
- typedef iterator Iterator;
- typedef const_iterator ConstIterator;
- inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
- iterator find(const Key &key)
+ template <typename K> iterator findImpl(const K &key)
{
if (isEmpty()) // prevents detaching shared null
return end();
@@ -1267,7 +1275,7 @@ public:
return end();
return iterator(it.toIterator(d));
}
- const_iterator find(const Key &key) const noexcept
+ template <typename K> const_iterator constFindImpl(const K &key) const noexcept
{
if (isEmpty())
return end();
@@ -1276,6 +1284,19 @@ public:
return end();
return const_iterator({d, it.toBucketIndex(d)});
}
+
+public:
+ typedef iterator Iterator;
+ typedef const_iterator ConstIterator;
+ inline qsizetype count() const noexcept { return d ? qsizetype(d->size) : 0; }
+ iterator find(const Key &key)
+ {
+ return findImpl(key);
+ }
+ const_iterator find(const Key &key) const noexcept
+ {
+ return constFindImpl(key);
+ }
const_iterator constFind(const Key &key) const noexcept
{
return find(key);
@@ -1339,8 +1360,65 @@ private:
result.it.node()->emplaceValue(std::forward<Args>(args)...);
return iterator(result.it);
}
-};
+public:
+#ifdef __cpp_concepts
+ bool remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return removeImpl(key);
+ }
+ T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return takeImpl(key);
+ }
+ bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return d ? d->findNode(key) != nullptr : false;
+ }
+ qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return contains(key) ? 1 : 0;
+ }
+ T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return valueImpl(key, [] { return T(); });
+ }
+ T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
+ {
+ return valueImpl(key, [&] { return defaultValue; });
+ }
+ T &operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return operatorIndexImpl(key);
+ }
+ const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return value(key);
+ }
+ std::pair<iterator, iterator>
+ equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return equal_range_impl(*this, key);
+ }
+ std::pair<const_iterator, const_iterator>
+ equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return equal_range_impl(*this, key);
+ }
+ iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return findImpl(key);
+ }
+ const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return constFindImpl(key);
+ }
+ const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return find(key);
+ }
+#endif // __cpp_concepts
+};
template <typename Key, typename T>
@@ -1518,6 +1596,11 @@ public:
qsizetype remove(const Key &key)
{
+ return removeImpl(key);
+ }
+private:
+ template <typename K> qsizetype removeImpl(const K &key)
+ {
if (isEmpty()) // prevents detaching shared null
return 0;
auto it = d->findBucket(key);
@@ -1533,13 +1616,21 @@ public:
d->erase(it);
return n;
}
+
+public:
template <typename Predicate>
qsizetype removeIf(Predicate pred)
{
return QtPrivate::associative_erase_if(*this, pred);
}
+
T take(const Key &key)
{
+ return takeImpl(key);
+ }
+private:
+ template <typename K> T takeImpl(const K &key)
+ {
if (isEmpty()) // prevents detaching shared null
return T();
auto it = d->findBucket(key);
@@ -1564,6 +1655,7 @@ public:
return t;
}
+public:
bool contains(const Key &key) const noexcept
{
if (!d)
@@ -1572,77 +1664,71 @@ public:
}
private:
- const Key *keyImpl(const T &value) const noexcept
+ template <typename Fn> Key keyImpl(const T &value, Fn &&defaultValue) const noexcept
{
if (d) {
auto i = d->begin();
while (i != d->end()) {
Chain *e = i.node()->value;
if (e->contains(value))
- return &i.node()->key;
+ return i.node()->key;
++i;
}
}
- return nullptr;
+ return defaultValue();
}
public:
Key key(const T &value) const noexcept
{
- if (auto *k = keyImpl(value))
- return *k;
- else
- return Key();
+ return keyImpl(value, [] { return Key(); });
}
Key key(const T &value, const Key &defaultKey) const noexcept
{
- if (auto *k = keyImpl(value))
- return *k;
- else
- return defaultKey;
+ return keyImpl(value, [&] { return defaultKey; });
}
private:
- T *valueImpl(const Key &key) const noexcept
+ template <typename K, typename Fn> T valueImpl(const K &key, Fn &&defaultValue) const noexcept
{
if (d) {
Node *n = d->findNode(key);
if (n) {
Q_ASSERT(n->value);
- return &n->value->value;
+ return n->value->value;
}
}
- return nullptr;
+ return defaultValue();
}
public:
T value(const Key &key) const noexcept
{
- if (auto *v = valueImpl(key))
- return *v;
- else
- return T();
+ return valueImpl(key, [] { return T(); });
}
T value(const Key &key, const T &defaultValue) const noexcept
{
- if (auto *v = valueImpl(key))
- return *v;
- else
- return defaultValue;
+ return valueImpl(key, [&] { return defaultValue; });
}
T &operator[](const Key &key)
{
+ return operatorIndexImpl(key);
+ }
+private:
+ template <typename K> T &operatorIndexImpl(const K &key)
+ {
const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
detach();
auto result = d->findOrInsert(key);
Q_ASSERT(!result.it.atEnd());
if (!result.initialized) {
- Node::createInPlace(result.it.node(), key, T());
+ Node::createInPlace(result.it.node(), Key(key), T());
++m_size;
}
return result.it.node()->value->value;
}
+public:
const T operator[](const Key &key) const noexcept
{
return value(key);
@@ -1673,9 +1759,15 @@ public:
}
return res;
}
+
QList<T> values() const { return QList<T>(begin(), end()); }
QList<T> values(const Key &key) const
{
+ return valuesImpl(key);
+ }
+private:
+ template <typename K> QList<T> valuesImpl(const K &key) const
+ {
QList<T> values;
if (d) {
Node *n = d->findNode(key);
@@ -1690,6 +1782,7 @@ public:
return values;
}
+public:
class const_iterator;
class iterator
@@ -1901,7 +1994,9 @@ public:
typedef iterator Iterator;
typedef const_iterator ConstIterator;
inline qsizetype count() const noexcept { return size(); }
- iterator find(const Key &key)
+
+private:
+ template <typename K> iterator findImpl(const K &key)
{
if (isEmpty())
return end();
@@ -1914,11 +2009,7 @@ public:
return end();
return iterator(it.toIterator(d));
}
- const_iterator find(const Key &key) const noexcept
- {
- return constFind(key);
- }
- const_iterator constFind(const Key &key) const noexcept
+ template <typename K> const_iterator constFindImpl(const K &key) const noexcept
{
if (isEmpty())
return end();
@@ -1927,6 +2018,20 @@ public:
return constEnd();
return const_iterator(it.toIterator(d));
}
+public:
+ iterator find(const Key &key)
+ {
+ return findImpl(key);
+ }
+ const_iterator constFind(const Key &key) const noexcept
+ {
+ return constFindImpl(key);
+ }
+ const_iterator find(const Key &key) const noexcept
+ {
+ return constFindImpl(key);
+ }
+
iterator insert(const Key &key, const T &value)
{
return emplace(key, value);
@@ -1992,6 +2097,11 @@ public:
bool contains(const Key &key, const T &value) const noexcept
{
+ return containsImpl(key, value);
+ }
+private:
+ template <typename K> bool containsImpl(const K &key, const T &value) const noexcept
+ {
if (isEmpty())
return false;
auto n = d->findNode(key);
@@ -2000,8 +2110,14 @@ public:
return n->value->contains(value);
}
+public:
qsizetype remove(const Key &key, const T &value)
{
+ return removeImpl(key, value);
+ }
+private:
+ template <typename K> qsizetype removeImpl(const K &key, const T &value)
+ {
if (isEmpty()) // prevents detaching shared null
return 0;
auto it = d->findBucket(key);
@@ -2030,8 +2146,14 @@ public:
return n;
}
+public:
qsizetype count(const Key &key) const noexcept
{
+ return countImpl(key);
+ }
+private:
+ template <typename K> qsizetype countImpl(const K &key) const noexcept
+ {
if (!d)
return 0;
auto it = d->findBucket(key);
@@ -2047,8 +2169,14 @@ public:
return n;
}
+public:
qsizetype count(const Key &key, const T &value) const noexcept
{
+ return countImpl(key, value);
+ }
+private:
+ template <typename K> qsizetype countImpl(const K &key, const T &value) const noexcept
+ {
if (!d)
return 0;
auto it = d->findBucket(key);
@@ -2065,7 +2193,7 @@ public:
return n;
}
- iterator find(const Key &key, const T &value)
+ template <typename K> iterator findImpl(const K &key, const T &value)
{
if (isEmpty())
return end();
@@ -2074,11 +2202,7 @@ public:
auto it = constFind(key, value);
return iterator(it.i, it.e);
}
- const_iterator find(const Key &key, const T &value) const noexcept
- {
- return constFind(key, value);
- }
- const_iterator constFind(const Key &key, const T &value) const noexcept
+ template <typename K> const_iterator constFindImpl(const K &key, const T &value) const noexcept
{
const_iterator i(constFind(key));
const_iterator end(constEnd());
@@ -2090,6 +2214,21 @@ public:
return end;
}
+public:
+ iterator find(const Key &key, const T &value)
+ {
+ return findImpl(key, value);
+ }
+
+ const_iterator constFind(const Key &key, const T &value) const noexcept
+ {
+ return constFindImpl(key, value);
+ }
+ const_iterator find(const Key &key, const T &value) const noexcept
+ {
+ return constFind(key, value);
+ }
+
QMultiHash &unite(const QMultiHash &other)
{
if (isEmpty()) {
@@ -2125,29 +2264,39 @@ public:
return *this;
}
- QPair<iterator, iterator> equal_range(const Key &key)
+ std::pair<iterator, iterator> equal_range(const Key &key)
+ {
+ return equal_range_impl(key);
+ }
+private:
+ template <typename K> std::pair<iterator, iterator> equal_range_impl(const K &key)
{
const auto copy = isDetached() ? QMultiHash() : *this; // keep 'key' alive across the detach
detach();
auto pair = std::as_const(*this).equal_range(key);
- return qMakePair(iterator(pair.first.i), iterator(pair.second.i));
+ return {iterator(pair.first.i), iterator(pair.second.i)};
}
- QPair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
+public:
+ std::pair<const_iterator, const_iterator> equal_range(const Key &key) const noexcept
+ {
+ return equal_range_impl(key);
+ }
+private:
+ template <typename K> std::pair<const_iterator, const_iterator> equal_range_impl(const K &key) const noexcept
{
if (!d)
- return qMakePair(end(), end());
+ return {end(), end()};
auto bucket = d->findBucket(key);
if (bucket.isUnused())
- return qMakePair(end(), end());
+ return {end(), end()};
auto it = bucket.toIterator(d);
auto end = it;
++end;
- return qMakePair(const_iterator(it), const_iterator(end));
+ return {const_iterator(it), const_iterator(end)};
}
-private:
void detach_helper()
{
if (!d) {
@@ -2184,6 +2333,94 @@ private:
}
return iterator(result.it);
}
+
+public:
+#ifdef __cpp_concepts
+ qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return removeImpl(key);
+ }
+ T take(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return takeImpl(key);
+ }
+ bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ if (!d)
+ return false;
+ return d->findNode(key) != nullptr;
+ }
+ T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return valueImpl(key, [] { return T(); });
+ }
+ T value(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &defaultValue) const noexcept
+ {
+ return valueImpl(key, [&] { return defaultValue; });
+ }
+ T &operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return operatorIndexImpl(key);
+ }
+ const T operator[](const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return value(key);
+ }
+ QList<T> values(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return valuesImpl(key);
+ }
+ iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return findImpl(key);
+ }
+ const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return constFindImpl(key);
+ }
+ const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return constFindImpl(key);
+ }
+ bool contains(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
+ {
+ return containsImpl(key, value);
+ }
+ qsizetype remove(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
+ {
+ return removeImpl(key, value);
+ }
+ qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return countImpl(key);
+ }
+ qsizetype count(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
+ {
+ return countImpl(key, value);
+ }
+ iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value)
+ {
+ return findImpl(key, value);
+ }
+ const_iterator constFind(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
+ {
+ return constFindImpl(key, value);
+ }
+ const_iterator find(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key, const T &value) const noexcept
+ {
+ return constFind(key, value);
+ }
+ std::pair<iterator, iterator>
+ equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key)
+ {
+ return equal_range_impl(key);
+ }
+ std::pair<const_iterator, const_iterator>
+ equal_range(const QHashPrivate::HeterogeneouslySearchableWith<Key> auto &key) const noexcept
+ {
+ return equal_range_impl(key);
+ }
+#endif // __cpp_concepts
};
Q_DECLARE_ASSOCIATIVE_FORWARD_ITERATOR(Hash)
diff --git a/src/corelib/tools/qhashfunctions.h b/src/corelib/tools/qhashfunctions.h
index bcb8caf1f7..90a269deaa 100644
--- a/src/corelib/tools/qhashfunctions.h
+++ b/src/corelib/tools/qhashfunctions.h
@@ -1,5 +1,6 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+// Copyright (C) 2024 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHASHFUNCTIONS_H
@@ -7,10 +8,14 @@
#include <QtCore/qstring.h>
#include <QtCore/qstringfwd.h>
-#include <QtCore/qpair.h>
#include <numeric> // for std::accumulate
#include <functional> // for std::hash
+#include <utility> // For std::pair
+
+#ifdef __cpp_concepts
+# include <concepts>
+#endif
#if 0
#pragma qt_class(QHashFunctions)
@@ -45,6 +50,21 @@ private:
size_t data;
};
+// Whether, ∀ t of type T && ∀ seed, qHash(Key(t), seed) == qHash(t, seed)
+template <typename Key, typename T> struct QHashHeterogeneousSearch : std::false_type {};
+
+// Specializations
+template <> struct QHashHeterogeneousSearch<QString, QStringView> : std::true_type {};
+template <> struct QHashHeterogeneousSearch<QStringView, QString> : std::true_type {};
+template <> struct QHashHeterogeneousSearch<QByteArray, QByteArrayView> : std::true_type {};
+template <> struct QHashHeterogeneousSearch<QByteArrayView, QByteArray> : std::true_type {};
+#ifndef Q_PROCESSOR_ARM
+template <> struct QHashHeterogeneousSearch<QString, QLatin1StringView> : std::true_type {};
+template <> struct QHashHeterogeneousSearch<QStringView, QLatin1StringView> : std::true_type {};
+template <> struct QHashHeterogeneousSearch<QLatin1StringView, QString> : std::true_type {};
+template <> struct QHashHeterogeneousSearch<QLatin1StringView, QStringView> : std::true_type {};
+#endif
+
namespace QHashPrivate {
Q_DECL_CONST_FUNCTION constexpr size_t hash(size_t key, size_t seed) noexcept
@@ -102,7 +122,39 @@ Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(quint64 key, size_t seed = 0
key ^= (key >> 32);
return QHashPrivate::hash(size_t(key), seed);
}
-Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(qint64 key, size_t seed = 0) noexcept { return qHash(quint64(key), seed); }
+Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(qint64 key, size_t seed = 0) noexcept
+{
+ if constexpr (sizeof(qint64) > sizeof(size_t)) {
+ // Avoid QTBUG-116080: we XOR the top half with its own sign bit:
+ // - if the qint64 is in range of qint32, then signmask ^ high == 0
+ // (for Qt 7 only)
+ // - if the qint64 is in range of quint32, then signmask == 0 and we
+ // do the same as the quint64 overload above
+ quint32 high = quint32(quint64(key) >> 32);
+ quint32 low = quint32(quint64(key));
+ quint32 signmask = qint32(high) >> 31; // all zeroes or all ones
+ signmask = QT_VERSION_MAJOR > 6 ? signmask : 0;
+ low ^= signmask ^ high;
+ return qHash(low, seed);
+ }
+ return qHash(quint64(key), seed);
+}
+#if QT_SUPPORTS_INT128
+constexpr size_t qHash(quint128 key, size_t seed = 0) noexcept
+{
+ return qHash(quint64(key + (key >> 64)), seed);
+}
+constexpr size_t qHash(qint128 key, size_t seed = 0) noexcept
+{
+ // Avoid QTBUG-116080: same as above, but with double the sizes and without
+ // the need for compatibility
+ quint64 high = quint64(quint128(key) >> 64);
+ quint64 low = quint64(quint128(key));
+ quint64 signmask = qint64(high) >> 63; // all zeroes or all ones
+ low += signmask ^ high;
+ return qHash(low, seed);
+}
+#endif // QT_SUPPORTS_INT128
Q_DECL_CONST_FUNCTION inline size_t qHash(float key, size_t seed = 0) noexcept
{
// ensure -0 gets mapped to 0
@@ -112,9 +164,7 @@ Q_DECL_CONST_FUNCTION inline size_t qHash(float key, size_t seed = 0) noexcept
return QHashPrivate::hash(k, seed);
}
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION size_t qHash(double key, size_t seed = 0) noexcept;
-#if !defined(Q_OS_DARWIN) || defined(Q_QDOC)
Q_CORE_EXPORT Q_DECL_CONST_FUNCTION size_t qHash(long double key, size_t seed = 0) noexcept;
-#endif
Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(wchar_t key, size_t seed = 0) noexcept
{ return QHashPrivate::hash(size_t(key), seed); }
Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(char16_t key, size_t seed = 0) noexcept
@@ -153,7 +203,9 @@ inline Q_DECL_PURE_FUNCTION size_t qHash(const QByteArray &key, size_t seed = 0
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(QStringView key, size_t seed = 0) noexcept;
inline Q_DECL_PURE_FUNCTION size_t qHash(const QString &key, size_t seed = 0) noexcept
{ return qHash(QStringView{key}, seed); }
+#ifndef QT_BOOTSTRAPPED
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(const QBitArray &key, size_t seed = 0) noexcept;
+#endif
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(QLatin1StringView key, size_t seed = 0) noexcept;
Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(QKeyCombination key, size_t seed = 0) noexcept
{ return qHash(key.toCombined(), seed); }
@@ -184,12 +236,35 @@ size_t qHash(const T &t, size_t seed) noexcept(noexcept(qHash(t)))
{ return qHash(t) ^ seed; }
#endif // < Qt 7
+namespace QHashPrivate {
+#ifdef __cpp_concepts
+template <typename Key, typename T> concept HeterogeneouslySearchableWithHelper =
+ // if Key and T are not the same (member already exists)
+ !std::is_same_v<Key, T>
+ // but are comparable amongst each other
+ && std::equality_comparable_with<Key, T>
+ // and supports heteregenous hashing
+ && QHashHeterogeneousSearch<Key, T>::value;
+template <typename Key, typename T> concept HeterogeneouslySearchableWith =
+ HeterogeneouslySearchableWithHelper<q20::remove_cvref_t<Key>, q20::remove_cvref_t<T>>;
+#else
+template <typename Key, typename T> constexpr bool HeterogeneouslySearchableWith = false;
+#endif
+}
+
template<typename T>
bool qHashEquals(const T &a, const T &b)
{
return a == b;
}
+template <typename T1, typename T2>
+std::enable_if_t<QHashPrivate::HeterogeneouslySearchableWith<T1, T2>, bool>
+qHashEquals(const T1 &a, const T2 &b)
+{
+ return a == b;
+}
+
namespace QtPrivate {
struct QHashCombine
@@ -326,7 +401,9 @@ QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QStringView)
QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QLatin1StringView)
QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_VALUE(QByteArrayView)
QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QByteArray)
+#ifndef QT_BOOTSTRAPPED
QT_SPECIALIZE_STD_HASH_TO_CALL_QHASH_BY_CREF(QBitArray)
+#endif
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qiterator.h b/src/corelib/tools/qiterator.h
index ab3d71f760..8a2b493ef4 100644
--- a/src/corelib/tools/qiterator.h
+++ b/src/corelib/tools/qiterator.h
@@ -5,6 +5,7 @@
#define QITERATOR_H
#include <QtCore/qglobal.h>
+#include <QtCore/qcontainertools_impl.h>
QT_BEGIN_NAMESPACE
@@ -253,26 +254,10 @@ public:
return std::pair<Key, T>(i.key(), i.value());
}
- struct pointer
- {
- pointer(value_type &&r_) : r(std::move(r_)) { }
-
- pointer() = default;
- pointer(const pointer &other) = default;
- pointer(pointer &&other) = default;
- pointer &operator=(const pointer &other) = default;
- pointer &operator=(pointer &&other) = default;
-
- value_type &operator*() const { return r; }
-
- value_type r;
- const value_type *operator->() const {
- return &r;
- }
- };
+ using pointer = QtPrivate::ArrowProxy<value_type>;
pointer operator->() const {
- return pointer(std::pair<Key, T>(i.key(), i.value()));
+ return pointer{std::pair<Key, T>(i.key(), i.value())};
}
friend bool operator==(QKeyValueIterator lhs, QKeyValueIterator rhs) noexcept { return lhs.i == rhs.i; }
diff --git a/src/corelib/tools/qiterator.qdoc b/src/corelib/tools/qiterator.qdoc
index f84d1cb3ba..041fb0701d 100644
--- a/src/corelib/tools/qiterator.qdoc
+++ b/src/corelib/tools/qiterator.qdoc
@@ -973,9 +973,8 @@
be preferred.
QMutableHashIterator\<Key, T\> allows you to iterate over a QHash
- (or a QMultiHash) and modify the hash. If you don't want to modify
- the hash (or have a const QHash), use the slightly faster
- QHashIterator instead.
+ and modify the hash. If you don't want to modify the hash (or have
+ a const QHash), use the slightly faster QHashIterator instead.
The QMutableHashIterator constructor takes a QHash as argument.
After construction, the iterator is located at the very beginning
diff --git a/src/corelib/tools/qline.cpp b/src/corelib/tools/qline.cpp
index 9216b8875b..e313b06aa9 100644
--- a/src/corelib/tools/qline.cpp
+++ b/src/corelib/tools/qline.cpp
@@ -14,6 +14,9 @@ QT_BEGIN_NAMESPACE
\class QLine
\inmodule QtCore
\ingroup painting
+ \compares equality
+ \compareswith equality QLineF
+ \endcompareswith
\brief The QLine class provides a two-dimensional vector using
integer precision.
@@ -133,18 +136,18 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn bool QLine::operator!=(const QLine &line) const
+ \fn bool QLine::operator!=(const QLine &lhs, const QLine &rhs)
- Returns \c true if the given \a line is not the same as \e this line.
+ Returns \c true if the line \a lhs is not the same as line \a rhs.
A line is different from another line if any of their start or
end points differ, or the internal order of the points is different.
*/
/*!
- \fn bool QLine::operator==(const QLine &line) const
+ \fn bool QLine::operator==(const QLine &lhs, const QLine &rhs)
- Returns \c true if the given \a line is the same as \e this line.
+ Returns \c true if the line \a lhs is the same as line \a rhs.
A line is identical to another line if the start and end points
are identical, and the internal order of the points is the same.
@@ -288,6 +291,9 @@ QDataStream &operator>>(QDataStream &stream, QLine &line)
\class QLineF
\inmodule QtCore
\ingroup painting
+ \compares equality
+ \compareswith equality QLine
+ \endcompareswith
\brief The QLineF class provides a two-dimensional vector using
floating point precision.
@@ -508,18 +514,18 @@ QDataStream &operator>>(QDataStream &stream, QLine &line)
*/
/*!
- \fn bool QLineF::operator!=(const QLineF &line) const
+ \fn bool QLineF::operator!=(const QLineF &lhs, const QLineF &rhs)
- Returns \c true if the given \a line is not the same as \e this line.
+ Returns \c true if the line \a lhs is not the same as line \a rhs.
A line is different from another line if their start or end points
differ, or the internal order of the points is different.
*/
/*!
- \fn bool QLineF::operator==(const QLineF &line) const
+ \fn bool QLineF::operator==(const QLineF &lhs, const QLineF &rhs)
- Returns \c true if the given \a line is the same as this line.
+ Returns \c true if the line \a lhs is the same as line \a rhs.
A line is identical to another line if the start and end points
are identical, and the internal order of the points is the same.
@@ -781,6 +787,25 @@ qreal QLineF::angleTo(const QLineF &l) const
return delta_normalized;
}
+/*!
+ \fn bool QLineF::qFuzzyCompare(const QLineF &lhs, const QLineF &rhs)
+ \since 6.8
+
+ Returns \c true if line \a lhs is approximately equal to line \a rhs;
+ otherwise returns \c false.
+
+ The lines are considered approximately equal if their start and end
+ points are approximately equal.
+*/
+
+/*!
+ \fn bool QLineF::qFuzzyIsNull(const QLineF &line)
+ \since 6.8
+
+ Returns \c true if the start point of line \a line is approximately
+ equal to its end point; otherwise returns \c false.
+*/
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QLineF &p)
{
diff --git a/src/corelib/tools/qline.h b/src/corelib/tools/qline.h
index e23ffbe9d5..03dac30e16 100644
--- a/src/corelib/tools/qline.h
+++ b/src/corelib/tools/qline.h
@@ -48,12 +48,20 @@ public:
inline void setPoints(const QPoint &p1, const QPoint &p2);
inline void setLine(int x1, int y1, int x2, int y2);
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool operator==(const QLine &d) const noexcept;
- constexpr inline bool operator!=(const QLine &d) const noexcept { return !(*this == d); }
+ constexpr inline bool operator!=(const QLine &d) const noexcept { return !operator==(d); }
+#endif
[[nodiscard]] constexpr inline QLineF toLineF() const noexcept;
private:
+ friend constexpr bool comparesEqual(const QLine &lhs, const QLine &rhs) noexcept
+ { return lhs.pt1 == rhs.pt1 && lhs.pt2 == rhs.pt2; }
+#if !QT_CORE_REMOVED_SINCE(6, 8)
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QLine)
+#endif
+
QPoint pt1, pt2;
};
Q_DECLARE_TYPEINFO(QLine, Q_PRIMITIVE_TYPE);
@@ -161,10 +169,12 @@ inline void QLine::setLine(int aX1, int aY1, int aX2, int aY2)
pt2 = QPoint(aX2, aY2);
}
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool QLine::operator==(const QLine &d) const noexcept
{
- return pt1 == d.pt1 && pt2 == d.pt2;
+ return comparesEqual(*this, d);
}
+#endif
#ifndef QT_NO_DEBUG_STREAM
Q_CORE_EXPORT QDebug operator<<(QDebug d, const QLine &p);
@@ -233,12 +243,30 @@ public:
inline void setPoints(const QPointF &p1, const QPointF &p2);
inline void setLine(qreal x1, qreal y1, qreal x2, qreal y2);
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool operator==(const QLineF &d) const;
- constexpr inline bool operator!=(const QLineF &d) const { return !(*this == d); }
+ constexpr inline bool operator!=(const QLineF &d) const { return !operator==(d); }
+#endif
constexpr QLine toLine() const;
private:
+ friend constexpr bool comparesEqual(const QLineF &lhs, const QLineF &rhs) noexcept
+ { return lhs.pt1 == rhs.pt1 && lhs.pt2 == rhs.pt2; }
+#if !QT_CORE_REMOVED_SINCE(6, 8)
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QLineF)
+#endif
+
+ friend constexpr bool comparesEqual(const QLineF &lhs, const QLine &rhs) noexcept
+ { return comparesEqual(lhs, rhs.toLineF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QLineF, QLine)
+
+ friend constexpr bool qFuzzyCompare(const QLineF &lhs, const QLineF &rhs) noexcept
+ { return qFuzzyCompare(lhs.pt1, rhs.pt1) && qFuzzyCompare(lhs.pt2, rhs.pt2); }
+
+ friend constexpr bool qFuzzyIsNull(const QLineF &line) noexcept
+ { return qFuzzyCompare(line.pt1, line.pt2); }
+
QPointF pt1, pt2;
};
Q_DECLARE_TYPEINFO(QLineF, Q_PRIMITIVE_TYPE);
@@ -283,7 +311,7 @@ constexpr inline qreal QLineF::y2() const
constexpr inline bool QLineF::isNull() const
{
- return qFuzzyCompare(pt1.x(), pt2.x()) && qFuzzyCompare(pt1.y(), pt2.y());
+ return qFuzzyCompare(pt1, pt2);
}
constexpr inline QPointF QLineF::p1() const
@@ -383,12 +411,12 @@ inline void QLineF::setLine(qreal aX1, qreal aY1, qreal aX2, qreal aY2)
pt2 = QPointF(aX2, aY2);
}
-
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool QLineF::operator==(const QLineF &d) const
{
- return pt1 == d.pt1 && pt2 == d.pt2;
+ return comparesEqual(*this, d);
}
-
+#endif
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h
index 79de8fced0..1cced5acc2 100644
--- a/src/corelib/tools/qlist.h
+++ b/src/corelib/tools/qlist.h
@@ -10,6 +10,8 @@
#include <QtCore/qhashfunctions.h>
#include <QtCore/qiterator.h>
#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qttypetraits.h>
#include <functional>
#include <limits>
@@ -116,8 +118,7 @@ public:
public:
using difference_type = qsizetype;
using value_type = T;
- // libstdc++ shipped with gcc < 11 does not have a fix for defect LWG 3346
-#if __cplusplus >= 202002L && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 11)
+#ifdef QT_COMPILER_HAS_LWG3346
using iterator_concept = std::contiguous_iterator_tag;
using element_type = value_type;
#endif
@@ -187,8 +188,7 @@ public:
public:
using difference_type = qsizetype;
using value_type = T;
- // libstdc++ shipped with gcc < 11 does not have a fix for defect LWG 3346
-#if __cplusplus >= 202002L && (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 11)
+#ifdef QT_COMPILER_HAS_LWG3346
using iterator_concept = std::contiguous_iterator_tag;
using element_type = const value_type;
#endif
@@ -259,6 +259,14 @@ private:
const std::less<const T*> less = {};
return !less(d->end(), i.i) && !less(i.i, d->begin());
}
+
+ void verify([[maybe_unused]] qsizetype pos = 0, [[maybe_unused]] qsizetype n = 1) const
+ {
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(pos <= size());
+ Q_ASSERT(n >= 0);
+ Q_ASSERT(n <= size() - pos);
+ }
public:
QList(DataPointer dd) noexcept
: d(dd)
@@ -268,20 +276,20 @@ public:
public:
QList() = default;
explicit QList(qsizetype size)
- : d(Data::allocate(size))
+ : d(size)
{
if (size)
d->appendInitialize(size);
}
QList(qsizetype size, parameter_type t)
- : d(Data::allocate(size))
+ : d(size)
{
if (size)
d->copyAppend(size, t);
}
inline QList(std::initializer_list<T> args)
- : d(Data::allocate(qsizetype(args.size())))
+ : d(qsizetype(args.size()))
{
if (args.size())
d->copyAppend(args.begin(), args.end());
@@ -289,10 +297,7 @@ public:
QList<T> &operator=(std::initializer_list<T> args)
{
- d = DataPointer(Data::allocate(qsizetype(args.size())));
- if (args.size())
- d->copyAppend(args.begin(), args.end());
- return *this;
+ return assign(args);
}
template <typename InputIterator, if_input_iterator<InputIterator> = true>
@@ -303,7 +308,7 @@ public:
} else {
const auto distance = std::distance(i1, i2);
if (distance) {
- d = DataPointer(Data::allocate(qsizetype(distance)));
+ d = DataPointer(qsizetype(distance));
// appendIteratorRange can deal with contiguous iterators on its own,
// this is an optimization for C++17 code.
if constexpr (std::is_same_v<std::decay_t<InputIterator>, iterator> ||
@@ -321,6 +326,13 @@ public:
inline explicit QList(const String &str)
{ append(str); }
+ QList(qsizetype size, Qt::Initialization)
+ : d(size)
+ {
+ if (size)
+ d->appendUninitialized(size);
+ }
+
// compiler-generated special member functions are fine!
void swap(QList &other) noexcept { d.swap(other.d); }
@@ -401,6 +413,12 @@ public:
if (size > this->size())
d->copyAppend(size - this->size(), c);
}
+ void resizeForOverwrite(qsizetype size)
+ {
+ resize_internal(size);
+ if (size > this->size())
+ d->appendUninitialized(size);
+ }
inline qsizetype capacity() const { return qsizetype(d->constAllocatedCapacity()); }
void reserve(qsizetype size);
@@ -419,7 +437,7 @@ public:
return;
if (d->needsDetach()) {
// must allocate memory
- DataPointer detached(Data::allocate(d.allocatedCapacity()));
+ DataPointer detached(d.allocatedCapacity());
d.swap(detached);
} else {
d->truncate(0);
@@ -636,27 +654,13 @@ public:
QList<T> mid(qsizetype pos, qsizetype len = -1) const;
QList<T> first(qsizetype n) const
- {
- Q_ASSERT(size_t(n) <= size_t(size()));
- return QList<T>(begin(), begin() + n);
- }
+ { verify(0, n); return QList<T>(begin(), begin() + n); }
QList<T> last(qsizetype n) const
- {
- Q_ASSERT(size_t(n) <= size_t(size()));
- return QList<T>(end() - n, end());
- }
+ { verify(0, n); return QList<T>(end() - n, end()); }
QList<T> sliced(qsizetype pos) const
- {
- Q_ASSERT(size_t(pos) <= size_t(size()));
- return QList<T>(begin() + pos, end());
- }
+ { verify(pos, 0); return QList<T>(begin() + pos, end()); }
QList<T> sliced(qsizetype pos, qsizetype n) const
- {
- Q_ASSERT(size_t(pos) <= size_t(size()));
- Q_ASSERT(n >= 0);
- Q_ASSERT(pos + n <= size());
- return QList<T>(begin() + pos, begin() + pos + n);
- }
+ { verify(pos, n); return QList<T>(begin() + pos, begin() + pos + n); }
T value(qsizetype i) const { return value(i, T()); }
T value(qsizetype i, parameter_type defaultValue) const;
@@ -686,6 +690,10 @@ public:
inline reference back() { return last(); }
inline const_reference back() const noexcept { return last(); }
void shrink_to_fit() { squeeze(); }
+ static qsizetype max_size() noexcept
+ {
+ return Data::max_size();
+ }
// comfort
QList<T> &operator+=(const QList<T> &l) { append(l); return *this; }
@@ -756,7 +764,7 @@ void QList<T>::reserve(qsizetype asize)
}
}
- DataPointer detached(Data::allocate(qMax(asize, size())));
+ DataPointer detached(qMax(asize, size()));
detached->copyAppend(d->begin(), d->end());
if (detached.d_ptr())
detached->setFlag(Data::CapacityReserved);
@@ -770,7 +778,7 @@ inline void QList<T>::squeeze()
return;
if (d->needsDetach() || size() < capacity()) {
// must allocate memory
- DataPointer detached(Data::allocate(size()));
+ DataPointer detached(size());
if (size()) {
if (d.needsDetach())
detached->copyAppend(d.data(), d.data() + d.size);
@@ -899,7 +907,7 @@ inline QList<T> &QList<T>::fill(parameter_type t, qsizetype newSize)
newSize = size();
if (d->needsDetach() || newSize > capacity()) {
// must allocate memory
- DataPointer detached(Data::allocate(d->detachCapacity(newSize)));
+ DataPointer detached(d->detachCapacity(newSize));
detached->copyAppend(newSize, t);
d.swap(detached);
} else {
@@ -981,7 +989,7 @@ inline QList<T> QList<T>::mid(qsizetype pos, qsizetype len) const
}
// Allocate memory
- DataPointer copied(Data::allocate(l));
+ DataPointer copied(l);
copied->copyAppend(data() + p, data() + p + l);
return copied;
}
diff --git a/src/corelib/tools/qlist.qdoc b/src/corelib/tools/qlist.qdoc
index 554d07ec1b..e07b74cd53 100644
--- a/src/corelib/tools/qlist.qdoc
+++ b/src/corelib/tools/qlist.qdoc
@@ -247,6 +247,31 @@
\sa resize()
*/
+/*! \fn template <typename T> QList<T>::QList(qsizetype size, Qt::Initialization)
+ \since 6.8
+
+ Constructs a list with an initial size of \a size elements.
+
+ QList will make an attempt at \b{not initializing} the elements.
+
+//! [qlist-uninitialized-strategy]
+ Specifically:
+
+ \list
+
+ \li if \c{T} has a constructor that accepts \c{Qt::Uninitialized},
+ that constructor will be used to initialize the elements;
+
+ \li otherwise, each element is default constructed. For
+ trivially constructible types (such as \c{int}, \c{float}, etc.)
+ this is equivalent to not initializing them.
+
+ \endlist
+//! [qlist-uninitialized-strategy]
+
+ \sa resizeForOverwrite()
+*/
+
/*! \fn template <typename T> QList<T>::QList(qsizetype size, parameter_type value)
Constructs a list with an initial size of \a size elements.
@@ -274,7 +299,7 @@
Constructs a list from the std::initializer_list given by \a args.
*/
-/*! \fn template <typename T> template<typename InputIterator, if_input_iterator<InputIterator>> QList<T>::QList(InputIterator first, InputIterator last)
+/*! \fn template<typename T> template <typename InputIterator, QList<T>::if_input_iterator<InputIterator> = true> QList<T>::QList(InputIterator first, InputIterator last)
\since 5.14
Constructs a list with the contents in the iterator range [\a first, \a last).
@@ -426,12 +451,13 @@
*/
/*! \fn template <typename T> void QList<T>::resize(qsizetype size)
+ \fn template <typename T> void QList<T>::resize(qsizetype size, parameter_type c)
\since 6.0
Sets the size of the list to \a size. If \a size is greater than the
current size, elements are added to the end; the new elements are
- initialized with a \l{default-constructed value}. If \a size is less
- than the current size, elements are removed from the end.
+ initialized with either a \l{default-constructed value} or \a c. If \a size
+ is less than the current size, elements are removed from the end.
If this list is not shared, the capacity() is preserved. Use squeeze()
to shed excess capacity.
@@ -443,6 +469,17 @@
\sa size()
*/
+/*! \fn template <typename T> void QList<T>::resizeForOverwrite(qsizetype size)
+ \since 6.8
+
+ Sets the size of the list to \a size. If \a size is less than the
+ current size, elements are removed from the end. If \a size is
+ greater than the current size, elements are added to the end; QList
+ will make an attempt at \b{not initializing} these new elements.
+
+ \include qlist.qdoc qlist-uninitialized-strategy
+*/
+
/*! \fn template <typename T> qsizetype QList<T>::capacity() const
Returns the maximum number of items that can be stored in the
@@ -1292,6 +1329,15 @@
returns \c false.
*/
+/*! \fn template <typename T> qsizetype QList<T>::max_size()
+ \since 6.8
+
+ This function is provided for STL compatibility.
+ It returns the maximum number of elements that the list can
+ theoretically hold. In practice, the number can be much smaller,
+ limited by the amount of memory available to the system.
+*/
+
/*! \fn template <typename T> QList<T> &QList<T>::operator+=(const QList<T> &other)
Appends the items of the \a other list to this list and
@@ -1538,7 +1584,7 @@
\sa erase
*/
-/*! \fn template <typename T> QList<T>::assign(qsizetype n, parameter_type t)
+/*! \fn template <typename T> QList<T>& QList<T>::assign(qsizetype n, parameter_type t)
\since 6.6
Replaces the contents of this list with \a n copies of \a t.
@@ -1549,7 +1595,7 @@
list or this list is shared.
*/
-/*! \fn template <typename T> template <typename InputIterator, if_input_iterator<InputIterator>> QList<T>::assign(InputIterator first, InputIterator last)
+/*! \fn template <typename T> template <typename InputIterator, QList<T>::if_input_iterator<InputIterator>> QList<T>& QList<T>::assign(InputIterator first, InputIterator last)
\since 6.6
Replaces the contents of this list with a copy of the elements in the
@@ -1569,7 +1615,7 @@
*this.
*/
-/*! \fn template <typename T> QList<T>::assign(std::initializer_list<T> l)
+/*! \fn template <typename T> QList<T>& QList<T>::assign(std::initializer_list<T> l)
\since 6.6
Replaces the contents of this list with a copy of the elements of
diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h
index 1a6506348f..326ae7d8a5 100644
--- a/src/corelib/tools/qmap.h
+++ b/src/corelib/tools/qmap.h
@@ -5,12 +5,14 @@
#ifndef QMAP_H
#define QMAP_H
+#include <QtCore/qhashfunctions.h>
#include <QtCore/qiterator.h>
#include <QtCore/qlist.h>
#include <QtCore/qrefcount.h>
#include <QtCore/qpair.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qshareddata_impl.h>
+#include <QtCore/qttypetraits.h>
#include <functional>
#include <initializer_list>
@@ -762,7 +764,7 @@ public:
return isEmpty();
}
- QPair<iterator, iterator> equal_range(const Key &akey)
+ std::pair<iterator, iterator> equal_range(const Key &akey)
{
const auto copy = d.isShared() ? *this : QMap(); // keep `key` alive across the detach
detach();
@@ -770,13 +772,38 @@ public:
return {iterator(result.first), iterator(result.second)};
}
- QPair<const_iterator, const_iterator> equal_range(const Key &akey) const
+ std::pair<const_iterator, const_iterator> equal_range(const Key &akey) const
{
if (!d)
return {};
auto result = d->m.equal_range(akey);
return {const_iterator(result.first), const_iterator(result.second)};
}
+
+private:
+#ifdef Q_QDOC
+ friend size_t qHash(const QMap &key, size_t seed = 0);
+#else
+# if defined(Q_CC_GHS) || defined (Q_CC_MSVC)
+ // GHS and MSVC tries to intantiate qHash() for the noexcept running into a
+ // non-SFINAE'ed hard error... Create an artificial SFINAE context as a
+ // work-around:
+ template <typename M, std::enable_if_t<std::is_same_v<M, QMap>, bool> = true>
+ friend QtPrivate::QHashMultiReturnType<typename M::key_type, typename M::mapped_type>
+# else
+ using M = QMap;
+ friend size_t
+# endif
+ qHash(const M &key, size_t seed = 0)
+ noexcept(QHashPrivate::noexceptPairHash<typename M::key_type, typename M::mapped_type>())
+ {
+ if (!key.d)
+ return seed;
+ // don't use qHashRange to avoid its compile-time overhead:
+ return std::accumulate(key.d->m.begin(), key.d->m.end(), seed,
+ QtPrivate::QHashCombine{});
+ }
+#endif // !Q_QDOC
};
Q_DECLARE_ASSOCIATIVE_ITERATOR(Map)
@@ -788,6 +815,7 @@ qsizetype erase_if(QMap<Key, T> &map, Predicate pred)
return QtPrivate::associative_erase_if(map, pred);
}
+
//
// QMultiMap
//
@@ -1492,7 +1520,7 @@ public:
// STL compatibility
inline bool empty() const { return isEmpty(); }
- QPair<iterator, iterator> equal_range(const Key &akey)
+ std::pair<iterator, iterator> equal_range(const Key &akey)
{
const auto copy = d.isShared() ? *this : QMultiMap(); // keep `key` alive across the detach
detach();
@@ -1500,7 +1528,7 @@ public:
return {iterator(result.first), iterator(result.second)};
}
- QPair<const_iterator, const_iterator> equal_range(const Key &akey) const
+ std::pair<const_iterator, const_iterator> equal_range(const Key &akey) const
{
if (!d)
return {};
diff --git a/src/corelib/tools/qmap.qdoc b/src/corelib/tools/qmap.qdoc
index bbf20ef02b..0cabf3df38 100644
--- a/src/corelib/tools/qmap.qdoc
+++ b/src/corelib/tools/qmap.qdoc
@@ -1,6 +1,6 @@
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
// Copyright (C) 2016 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\class QMap
@@ -390,7 +390,7 @@
use that entails can be avoided by iterating from \l keyBegin() to
\l keyEnd().
- \sa QMultiMap::uniqueKeys(), values(), key()
+ \sa values(), key()
*/
/*! \fn template <class Key, class T> QList<Key> QMap<Key, T>::keys(const T &value) const
@@ -730,7 +730,7 @@
If there is already an item with the key \a key, that item's value
is replaced with \a value.
- \sa QMultiMap::insert()
+ Returns an iterator pointing to the new/updated element.
*/
/*! \fn template <class Key, class T> QMap<Key, T>::iterator QMap<Key, T>::insert(const_iterator pos, const Key &key, const T &value)
@@ -756,7 +756,7 @@
\b {Note:} Be careful with the hint. Providing an iterator from an older shared instance might
crash but there is also a risk that it will silently corrupt both the map and the \a pos map.
- \sa QMultiMap::insert()
+ Returns an iterator pointing to the new/updated element.
*/
/*! \fn template <class Key, class T> void QMap<Key, T>::insert(const QMap<Key, T> &map)
@@ -766,8 +766,6 @@
If a key is common to both maps, its value will be replaced with
the value stored in \a map.
-
- \sa QMultiMap::insert()
*/
/*! \fn template <class Key, class T> void QMap<Key, T>::insert(QMap<Key, T> &&map)
@@ -820,14 +818,14 @@
*/
/*!
- \fn template <class Key, class T> QPair<typename QMap<Key, T>::iterator, typename QMap<Key, T>::iterator> QMap<Key, T>::equal_range(const Key &key)
+ \fn template <class Key, class T> std::pair<typename QMap<Key, T>::iterator, typename QMap<Key, T>::iterator> QMap<Key, T>::equal_range(const Key &key)
Returns a pair of iterators delimiting the range of values \c{[first, second)}, that
are stored under \a key.
*/
/*!
- \fn template <class Key, class T> QPair<typename QMap<Key, T>::const_iterator, typename QMap<Key, T>::const_iterator> QMap<Key, T>::equal_range(const Key &key) const
+ \fn template <class Key, class T> std::pair<typename QMap<Key, T>::const_iterator, typename QMap<Key, T>::const_iterator> QMap<Key, T>::equal_range(const Key &key) const
\overload
\since 5.6
*/
@@ -1391,3 +1389,12 @@
Returns the number of elements removed, if any.
*/
+
+/*!
+ \fn template <class Key, class T> size_t QMap<Key, T>::qHash(const QMap &key, size_t seed) noexcept
+ \since 6.8
+
+ Returns the hash value for \a key, using \a seed to seed the calculation.
+
+ Types \c Key and \c T must be supported by qHash().
+*/
diff --git a/src/corelib/tools/qmargins.cpp b/src/corelib/tools/qmargins.cpp
index 1d2cb7d6e5..c4cd0da30d 100644
--- a/src/corelib/tools/qmargins.cpp
+++ b/src/corelib/tools/qmargins.cpp
@@ -14,6 +14,10 @@ QT_BEGIN_NAMESPACE
\ingroup painting
\since 4.6
+ \compares equality
+ \compareswith equality QMarginsF
+ \endcompareswith
+
\brief The QMargins class defines the four margins of a rectangle.
QMargin defines a set of four margins; left, top, right, and bottom,
@@ -107,15 +111,15 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn bool QMargins::operator==(const QMargins &m1, const QMargins &m2)
+ \fn bool QMargins::operator==(const QMargins &lhs, const QMargins &rhs)
- Returns \c true if \a m1 and \a m2 are equal; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
*/
/*!
- \fn bool QMargins::operator!=(const QMargins &m1, const QMargins &m2)
+ \fn bool QMargins::operator!=(const QMargins &lhs, const QMargins &rhs)
- Returns \c true if \a m1 and \a m2 are different; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are different; otherwise returns \c false.
*/
/*!
@@ -438,6 +442,10 @@ QDebug operator<<(QDebug dbg, const QMargins &m)
\ingroup painting
\since 5.3
+ \compares equality
+ \compareswith equality QMargins
+ \endcompareswith
+
\brief The QMarginsF class defines the four margins of a rectangle.
QMarginsF defines a set of four margins; left, top, right, and bottom,
@@ -746,6 +754,22 @@ QDebug operator<<(QDebug dbg, const QMargins &m)
\sa QMarginsF(), QMargins::toMarginsF()
*/
+/*!
+ \fn bool QMarginsF::qFuzzyCompare(const QMarginsF &lhs, const QMarginsF &rhs)
+ \since 6.8
+
+ Returns \c true if \a lhs is approximately equal to \a rhs;
+ otherwise returns \c false.
+*/
+
+/*!
+ \fn bool QMarginsF::qFuzzyIsNull(const QMarginsF &margins)
+ \since 6.8
+
+ Returns \c true if all components of margsins \a margins are
+ approximately equal to zero; otherwise returns \c false.
+*/
+
/*****************************************************************************
QMarginsF stream functions
*****************************************************************************/
diff --git a/src/corelib/tools/qmargins.h b/src/corelib/tools/qmargins.h
index f8d7150dfd..3b29860d66 100644
--- a/src/corelib/tools/qmargins.h
+++ b/src/corelib/tools/qmargins.h
@@ -4,6 +4,7 @@
#ifndef QMARGINS_H
#define QMARGINS_H
+#include <QtCore/qcompare.h>
#include <QtCore/qnamespace.h>
#include <QtCore/q20type_traits.h>
@@ -54,19 +55,14 @@ private:
int m_right;
int m_bottom;
- friend constexpr inline bool operator==(const QMargins &m1, const QMargins &m2) noexcept
+ friend constexpr bool comparesEqual(const QMargins &lhs, const QMargins &rhs) noexcept
{
- return
- m1.m_left == m2.m_left &&
- m1.m_top == m2.m_top &&
- m1.m_right == m2.m_right &&
- m1.m_bottom == m2.m_bottom;
- }
-
- friend constexpr inline bool operator!=(const QMargins &m1, const QMargins &m2) noexcept
- {
- return !(m1 == m2);
+ return lhs.m_left == rhs.m_left
+ && lhs.m_top == rhs.m_top
+ && lhs.m_right == rhs.m_right
+ && lhs.m_bottom == rhs.m_bottom;
}
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QMargins)
template <std::size_t I,
typename M,
@@ -304,18 +300,35 @@ private:
qreal m_right;
qreal m_bottom;
- friend constexpr inline bool operator==(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_FLOAT_COMPARE
+ friend constexpr bool qFuzzyCompare(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
{
- return qFuzzyCompare(lhs.left(), rhs.left())
- && qFuzzyCompare(lhs.top(), rhs.top())
- && qFuzzyCompare(lhs.right(), rhs.right())
- && qFuzzyCompare(lhs.bottom(), rhs.bottom());
+ 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));
+ }
+ QT_WARNING_POP
+ friend constexpr bool qFuzzyIsNull(const QMarginsF &m) noexcept
+ {
+ return qFuzzyIsNull(m.m_left) && qFuzzyIsNull(m.m_top)
+ && qFuzzyIsNull(m.m_right) && qFuzzyIsNull(m.m_bottom);
}
- friend constexpr inline bool operator!=(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
+ friend constexpr bool comparesEqual(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
{
- return !(lhs == rhs);
+ return qFuzzyCompare(lhs, rhs);
}
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QMarginsF)
+
+ friend constexpr bool comparesEqual(const QMarginsF &lhs, const QMargins &rhs) noexcept
+ { return comparesEqual(lhs, rhs.toMarginsF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QMarginsF, QMargins)
template <std::size_t I,
typename M,
diff --git a/src/corelib/tools/qmessageauthenticationcode.h b/src/corelib/tools/qmessageauthenticationcode.h
index 4e88138763..7c5edfa7bf 100644
--- a/src/corelib/tools/qmessageauthenticationcode.h
+++ b/src/corelib/tools/qmessageauthenticationcode.h
@@ -55,6 +55,30 @@ public:
static QByteArray hash(QByteArrayView message, QByteArrayView key,
QCryptographicHash::Algorithm method);
+ static QByteArrayView
+ hashInto(QSpan<char> buffer, QByteArrayView message, QByteArrayView key,
+ QCryptographicHash::Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), {&message, 1}, key, method); }
+ static QByteArrayView
+ hashInto(QSpan<uchar> buffer, QByteArrayView message, QByteArrayView key,
+ QCryptographicHash::Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), {&message, 1}, key, method); }
+ static QByteArrayView
+ hashInto(QSpan<std::byte> buffer, QByteArrayView message,
+ QByteArrayView key, QCryptographicHash::Algorithm method) noexcept
+ { return hashInto(buffer, {&message, 1}, key, method); }
+ static QByteArrayView
+ hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> messageParts,
+ QByteArrayView key, QCryptographicHash::Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), messageParts, key, method); }
+ static QByteArrayView
+ hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> messageParts,
+ QByteArrayView key, QCryptographicHash::Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), messageParts, key, method); }
+ static QByteArrayView
+ hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> message,
+ QByteArrayView key, QCryptographicHash::Algorithm method) noexcept;
+
private:
Q_DISABLE_COPY(QMessageAuthenticationCode)
QMessageAuthenticationCodePrivate *d;
diff --git a/src/corelib/tools/qminimalflatset_p.h b/src/corelib/tools/qminimalflatset_p.h
new file mode 100644
index 0000000000..6074688f6e
--- /dev/null
+++ b/src/corelib/tools/qminimalflatset_p.h
@@ -0,0 +1,156 @@
+// Copyright (C) 2022 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
+
+#ifndef QTCORE_QMINIMALFLATSET_P_H
+#define QTCORE_QMINIMALFLATSET_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qcontainerfwd.h>
+#include <QtCore/qfunctionaltools_impl.h> // CompactStorage
+#include <QtCore/private/qglobal_p.h>
+
+//#define QMINIMAL_FLAT_SET_DEBUG
+#ifdef QMINIMAL_FLAT_SET_DEBUG
+# include <QtCore/qscopeguard.h>
+# include <QtCore/qdebug.h>
+# define QMINIMAL_FLAT_SET_PRINT_AT_END \
+ const auto sg = qScopeGuard([&] { qDebug() << this << *this; });
+#else
+# define QMINIMAL_FLAT_SET_PRINT_AT_END
+#endif
+
+#include <algorithm> // for std::lower_bound
+#include <functional> // for std::less, std::ref
+
+QT_BEGIN_NAMESPACE
+
+/*
+ This is a minimal version of a QFlatSet, the std::set version of QFlatMap.
+ Like QFlatMap, it has linear insertion and removal, not logarithmic, like
+ real QMap and std::set, so it's only a good container if you either have
+ very few entries or lots, but with separate setup and lookup stages.
+ Because a full QFlatSet would be 10x the work on writing this minimal one,
+ we keep it here for now. When more users pop up and the class has matured a
+ bit, we can consider moving it as QFlatSet alongside QFlatMap.
+*/
+
+template <typename T, typename Container = QList<T>, typename Compare = std::less<T>>
+class QMinimalFlatSet : QtPrivate::CompactStorage<Compare>
+{
+ Container c;
+ using CompareStorage = QtPrivate::CompactStorage<Compare>;
+public:
+ QMinimalFlatSet() = default;
+ explicit QMinimalFlatSet(const Compare &cmp) : CompareStorage{cmp} {}
+ // Rule Of Zero applies
+
+ using const_iterator = typename Container::const_iterator;
+ using iterator = const_iterator;
+ using const_reverse_iterator = typename Container::const_reverse_iterator;
+ using reverse_iterator = const_reverse_iterator;
+ using value_type = T;
+ using key_compare = Compare;
+ using value_compare = Compare;
+
+ key_compare key_comp() const { return this->object(); }
+ value_compare value_comp() const { return key_comp(); }
+
+ iterator begin() const { return c.cbegin(); }
+ iterator end() const { return c.cend(); }
+ iterator cbegin() const { return begin(); }
+ iterator cend() const { return cend(); }
+
+ reverse_iterator rbegin() const { return c.crbegin(); }
+ reverse_iterator rend() const { return c.crend(); }
+ reverse_iterator crbegin() const { return rbegin(); }
+ reverse_iterator crend() const { return rend(); }
+
+ void clear() {
+ QMINIMAL_FLAT_SET_PRINT_AT_END
+ c.clear();
+ }
+ auto size() const { return c.size(); }
+ auto count() const { return size(); }
+ bool isEmpty() const { return size() == 0; }
+
+ std::pair<iterator, bool> insert(value_type &&v)
+ {
+ QMINIMAL_FLAT_SET_PRINT_AT_END
+ const auto r = lookup(v);
+ if (r.exists)
+ return {r.it, false};
+ else
+ return {c.insert(r.it, std::move(v)), true};
+ }
+
+ std::pair<iterator, bool> insert(const value_type &v)
+ {
+ QMINIMAL_FLAT_SET_PRINT_AT_END
+ const auto r = lookup(v);
+ if (r.exists)
+ return {r.it, false};
+ else
+ return {c.insert(r.it, v), true};
+ }
+
+ void erase(const value_type &v)
+ {
+ QMINIMAL_FLAT_SET_PRINT_AT_END
+ const auto r = lookup(v);
+ if (r.exists)
+ c.erase(r.it);
+ }
+ void remove(const value_type &v) { erase(v); }
+
+ bool contains(const value_type &v) const
+ {
+ return lookup(v).exists;
+ }
+
+ const Container &values() const & { return c; }
+ Container values() && { return std::move(c); }
+
+private:
+ auto lookup(const value_type &v) const
+ {
+ struct R {
+ iterator it;
+ bool exists;
+ };
+
+ auto cmp = std::ref(this->object()); // don't let std::lower_bound copy it
+
+ const auto it = std::lower_bound(c.cbegin(), c.cend(), v, cmp);
+ return R{it, it != c.cend() && !cmp(v, *it)};
+ }
+
+#ifdef QMINIMAL_FLAT_SET_DEBUG
+ friend QDebug operator<<(QDebug dbg, const QMinimalFlatSet &set)
+ {
+ const QDebugStateSaver saver(dbg);
+ dbg.nospace() << "QMinimalFlatSet{";
+ for (auto &e : set)
+ dbg << e << ", ";
+ return dbg << "}";
+ }
+#endif
+};
+
+#undef QMINIMAL_FLAT_SET_PRINT_AT_END
+
+template <typename T, qsizetype N = QVarLengthArrayDefaultPrealloc>
+using QMinimalVarLengthFlatSet = QMinimalFlatSet<T, QVarLengthArray<T, N>>;
+
+QT_END_NAMESPACE
+
+#endif // QTCORE_QMINIMALFLATSET_P_H
diff --git a/src/corelib/tools/qmultimap.qdoc b/src/corelib/tools/qmultimap.qdoc
index 5bc4af0940..0b05192817 100644
--- a/src/corelib/tools/qmultimap.qdoc
+++ b/src/corelib/tools/qmultimap.qdoc
@@ -1,6 +1,6 @@
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
// Copyright (C) 2020 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
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\class QMultiMap
@@ -817,6 +817,8 @@
different from replace(), which overwrites the value of an
existing item.)
+ Returns an iterator pointing to the new element.
+
\sa replace()
*/
@@ -839,6 +841,8 @@
is faster than inserting in sorted order with constEnd(), since constEnd() - 1 (which is needed
to check if the hint is valid) needs \l{logarithmic time}.
+ Returns an iterator pointing to the new element.
+
\b {Note:} Be careful with the hint. Providing an iterator from an older shared instance might
crash but there is also a risk that it will silently corrupt both the multi map and the \a pos multi map.
*/
@@ -886,6 +890,8 @@
If there are multiple items with the key \a key, the most
recently inserted item's value is replaced with \a value.
+ Returns an iterator pointing to the new/updated element.
+
\sa insert()
*/
@@ -928,14 +934,14 @@
*/
/*!
- \fn template <class Key, class T> QPair<typename QMultiMap<Key, T>::iterator, typename QMultiMap<Key, T>::iterator> QMultiMap<Key, T>::equal_range(const Key &key)
+ \fn template <class Key, class T> std::pair<typename QMultiMap<Key, T>::iterator, typename QMultiMap<Key, T>::iterator> QMultiMap<Key, T>::equal_range(const Key &key)
Returns a pair of iterators delimiting the range of values \c{[first, second)}, that
are stored under \a key.
*/
/*!
- \fn template <class Key, class T> QPair<typename QMultiMap<Key, T>::const_iterator, typename QMultiMap<Key, T>::const_iterator> QMultiMap<Key, T>::equal_range(const Key &key) const
+ \fn template <class Key, class T> std::pair<typename QMultiMap<Key, T>::const_iterator, typename QMultiMap<Key, T>::const_iterator> QMultiMap<Key, T>::equal_range(const Key &key) const
\overload
\since 5.6
*/
diff --git a/src/corelib/tools/qpair.h b/src/corelib/tools/qpair.h
index 8070ca0175..84f99075e1 100644
--- a/src/corelib/tools/qpair.h
+++ b/src/corelib/tools/qpair.h
@@ -4,6 +4,7 @@
#ifndef QPAIR_H
#define QPAIR_H
+#include <QtCore/qcontainerfwd.h>
#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
@@ -12,8 +13,7 @@ QT_BEGIN_NAMESPACE
#pragma qt_class(QPair)
#endif
-template <typename T1, typename T2>
-using QPair = std::pair<T1, T2>;
+#ifndef QT_NO_QPAIR
template <typename T1, typename T2>
constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2)
@@ -22,6 +22,8 @@ constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2)
return std::make_pair(std::forward<T1>(value1), std::forward<T2>(value2));
}
+#endif // QT_NO_QPAIR
+
QT_END_NAMESPACE
#endif // QPAIR_H
diff --git a/src/corelib/tools/qpoint.cpp b/src/corelib/tools/qpoint.cpp
index d1f3b12a68..775a354469 100644
--- a/src/corelib/tools/qpoint.cpp
+++ b/src/corelib/tools/qpoint.cpp
@@ -15,6 +15,10 @@ QT_BEGIN_NAMESPACE
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QPointF
+ \endcompareswith
+
\brief The QPoint class defines a point in the plane using integer
precision.
@@ -208,16 +212,17 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn bool QPoint::operator==(const QPoint &p1, const QPoint &p2)
+ \fn bool QPoint::operator==(const QPoint &lhs, const QPoint &rhs)
- Returns \c true if \a p1 and \a p2 are equal; otherwise returns
- false.
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns
+ \c false.
*/
/*!
- \fn bool QPoint::operator!=(const QPoint &p1, const QPoint &p2)
+ \fn bool QPoint::operator!=(const QPoint &lhs, const QPoint &rhs)
- Returns \c true if \a p1 and \a p2 are not equal; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are not equal; otherwise returns
+ \c false.
*/
/*!
@@ -463,6 +468,10 @@ size_t qHash(QPoint key, size_t seed) noexcept
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QPoint
+ \endcompareswith
+
\brief The QPointF class defines a point in the plane using
floating point precision.
@@ -730,9 +739,9 @@ size_t qHash(QPoint key, size_t seed) noexcept
*/
/*!
- \fn bool QPointF::operator==(const QPointF &p1, const QPointF &p2)
+ \fn bool QPointF::operator==(const QPointF &lhs, const QPointF &rhs)
- Returns \c true if \a p1 is approximately equal to \a p2; otherwise
+ Returns \c true if \a lhs is approximately equal to \a rhs; otherwise
returns \c false.
\warning This function does not check for strict equality; instead,
@@ -742,9 +751,9 @@ size_t qHash(QPoint key, size_t seed) noexcept
*/
/*!
- \fn bool QPointF::operator!=(const QPointF &p1, const QPointF &p2);
+ \fn bool QPointF::operator!=(const QPointF &lhs, const QPointF &rhs)
- Returns \c true if \a p1 is sufficiently different from \a p2;
+ Returns \c true if \a lhs is sufficiently different from \a rhs;
otherwise returns \c false.
\warning This function does not check for strict inequality; instead,
@@ -753,6 +762,26 @@ size_t qHash(QPoint key, size_t seed) noexcept
\sa qFuzzyCompare
*/
+/*!
+ \fn bool QPointF::qFuzzyCompare(const QPointF &p1, const QPointF &p2)
+ \since 6.8
+
+ Returns \c true if \a p1 is approximately equal to \a p2; otherwise
+ returns \c false.
+
+ \sa qFuzzyIsNull
+*/
+
+/*!
+ \fn bool QPointF::qFuzzyIsNull(const QPointF &point)
+ \since 6.8
+
+ Returns \c true if \a point is approximately equal to a point
+ \c {(0.0, 0.0)}.
+
+ \sa qFuzzyCompare
+*/
+
#ifndef QT_NO_DATASTREAM
/*!
\fn QDataStream &operator<<(QDataStream &stream, const QPointF &point)
diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h
index 7df4d49005..50b4c864be 100644
--- a/src/corelib/tools/qpoint.h
+++ b/src/corelib/tools/qpoint.h
@@ -4,7 +4,9 @@
#ifndef QPOINT_H
#define QPOINT_H
+#include <QtCore/qcompare.h>
#include <QtCore/qnamespace.h>
+#include <QtCore/qnumeric.h>
#include <QtCore/q20type_traits.h>
#include <QtCore/q23utility.h>
@@ -51,10 +53,10 @@ public:
constexpr static inline int dotProduct(const QPoint &p1, const QPoint &p2)
{ return p1.xp * p2.xp + p1.yp * p2.yp; }
- friend constexpr inline bool operator==(const QPoint &p1, const QPoint &p2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QPoint &p1, const QPoint &p2) noexcept
{ return p1.xp == p2.xp && p1.yp == p2.yp; }
- friend constexpr inline bool operator!=(const QPoint &p1, const QPoint &p2) noexcept
- { return p1.xp != p2.xp || p1.yp != p2.yp; }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QPoint)
friend constexpr inline QPoint operator+(const QPoint &p1, const QPoint &p2) noexcept
{ return QPoint(p1.xp + p2.xp, p1.yp + p2.yp); }
friend constexpr inline QPoint operator-(const QPoint &p1, const QPoint &p2) noexcept
@@ -78,6 +80,7 @@ public:
friend constexpr inline QPoint operator/(const QPoint &p, qreal c)
{ return QPoint(qRound(p.xp / c), qRound(p.yp / c)); }
+public:
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
[[nodiscard]] Q_CORE_EXPORT CGPoint toCGPoint() const noexcept;
#endif
@@ -241,19 +244,25 @@ public:
return p1.xp * p2.xp + p1.yp * p2.yp;
}
+private:
QT_WARNING_PUSH
QT_WARNING_DISABLE_FLOAT_COMPARE
- friend constexpr inline bool operator==(const QPointF &p1, const QPointF &p2)
+ 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));
}
- friend constexpr inline bool operator!=(const QPointF &p1, const QPointF &p2)
+ QT_WARNING_POP
+ friend constexpr bool qFuzzyIsNull(const QPointF &point) noexcept
{
- return !(p1 == p2);
+ return qFuzzyIsNull(point.xp) && qFuzzyIsNull(point.yp);
}
- QT_WARNING_POP
-
+ friend constexpr bool comparesEqual(const QPointF &p1, const QPointF &p2) noexcept
+ { return qFuzzyCompare(p1, p2); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QPointF)
+ friend constexpr bool comparesEqual(const QPointF &p1, const QPoint &p2) noexcept
+ { return comparesEqual(p1, p2.toPointF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QPointF, QPoint)
friend constexpr inline QPointF operator+(const QPointF &p1, const QPointF &p2)
{ return QPointF(p1.xp + p2.xp, p1.yp + p2.yp); }
friend constexpr inline QPointF operator-(const QPointF &p1, const QPointF &p2)
@@ -272,6 +281,7 @@ public:
return QPointF(p.xp / divisor, p.yp / divisor);
}
+public:
constexpr QPoint toPoint() const;
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
diff --git a/src/corelib/tools/qrect.cpp b/src/corelib/tools/qrect.cpp
index 6d345ce543..ce28a6d887 100644
--- a/src/corelib/tools/qrect.cpp
+++ b/src/corelib/tools/qrect.cpp
@@ -15,6 +15,10 @@ QT_BEGIN_NAMESPACE
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QRectF
+ \endcompareswith
+
\brief The QRect class defines a rectangle in the plane using
integer precision.
@@ -1105,18 +1109,18 @@ bool QRect::intersects(const QRect &r) const noexcept
}
/*!
- \fn bool QRect::operator==(const QRect &r1, const QRect &r2)
+ \fn bool QRect::operator==(const QRect &lhs, const QRect &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are equal,
+ Returns \c true if the rectangles \a lhs and \a rhs are equal,
otherwise returns \c false.
*/
/*!
- \fn bool QRect::operator!=(const QRect &r1, const QRect &r2)
+ \fn bool QRect::operator!=(const QRect &lhs, const QRect &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are different, otherwise
- returns \c false.
+ Returns \c true if the rectangles \a lhs and \a rhs are different,
+ otherwise returns \c false.
*/
/*!
@@ -1279,6 +1283,10 @@ QDebug operator<<(QDebug dbg, const QRect &r)
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QRect
+ \endcompareswith
+
\brief The QRectF class defines a finite rectangle in the plane using
floating point precision.
@@ -2346,10 +2354,10 @@ QRect QRectF::toAlignedRect() const noexcept
*/
/*!
- \fn bool QRectF::operator==(const QRectF &r1, const QRectF &r2)
+ \fn bool QRectF::operator==(const QRectF &lhs, const QRectF &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are \b approximately equal,
- otherwise returns \c false.
+ Returns \c true if the rectangles \a lhs and \a rhs are \b approximately
+ equal, otherwise returns \c false.
\warning This function does not check for strict equality; instead,
it uses a fuzzy comparison to compare the rectangles' coordinates.
@@ -2359,9 +2367,9 @@ QRect QRectF::toAlignedRect() const noexcept
/*!
- \fn bool QRectF::operator!=(const QRectF &r1, const QRectF &r2)
+ \fn bool QRectF::operator!=(const QRectF &lhs, const QRectF &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are sufficiently
+ Returns \c true if the rectangles \a lhs and \a rhs are sufficiently
different, otherwise returns \c false.
\warning This function does not check for strict inequality; instead,
@@ -2429,6 +2437,22 @@ QRect QRectF::toAlignedRect() const noexcept
\sa marginsRemoved(), operator+=(), marginsAdded()
*/
+/*!
+ \fn bool QRectF::qFuzzyCompare(const QRectF &lhs, const QRectF &rhs)
+ \since 6.8
+
+ Returns \c true if the rectangle \a lhs is approximately equal to the
+ rectangle \a rhs; otherwise returns \c false.
+*/
+
+/*!
+ \fn bool QRectF::qFuzzyIsNull(const QRectF &rect)
+ \since 6.8
+
+ Returns \c true if both width and height of the rectangle \a rect are
+ approximately equal to zero; otherwise returns \c false.
+*/
+
/*****************************************************************************
QRectF stream functions
*****************************************************************************/
diff --git a/src/corelib/tools/qrect.h b/src/corelib/tools/qrect.h
index e69a217f48..fb938b0056 100644
--- a/src/corelib/tools/qrect.h
+++ b/src/corelib/tools/qrect.h
@@ -119,12 +119,13 @@ public:
[[nodiscard]] static constexpr inline QRect span(const QPoint &p1, const QPoint &p2) noexcept;
- friend constexpr inline bool operator==(const QRect &r1, const QRect &r2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QRect &r1, const QRect &r2) noexcept
{ return r1.x1==r2.x1 && r1.x2==r2.x2 && r1.y1==r2.y1 && r1.y2==r2.y2; }
- friend constexpr inline bool operator!=(const QRect &r1, const QRect &r2) noexcept
- { return r1.x1!=r2.x1 || r1.x2!=r2.x2 || r1.y1!=r2.y1 || r1.y2!=r2.y2; }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QRect)
friend constexpr inline size_t qHash(const QRect &, size_t) noexcept;
+public:
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
[[nodiscard]] CGRect toCGRect() const noexcept;
#endif
@@ -572,17 +573,30 @@ public:
constexpr inline QRectF &operator+=(const QMarginsF &margins) noexcept;
constexpr inline QRectF &operator-=(const QMarginsF &margins) noexcept;
- friend constexpr inline bool operator==(const QRectF &r1, const QRectF &r2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QRectF &r1, const QRectF &r2) noexcept
{
return r1.topLeft() == r2.topLeft()
&& r1.size() == r2.size();
}
- friend constexpr inline bool operator!=(const QRectF &r1, const QRectF &r2) noexcept
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QRectF)
+
+ friend constexpr bool comparesEqual(const QRectF &r1, const QRect &r2) noexcept
+ { return r1.topLeft() == r2.topLeft() && r1.size() == r2.size(); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QRectF, QRect)
+
+ friend constexpr bool qFuzzyCompare(const QRectF &lhs, const QRectF &rhs) noexcept
{
- return r1.topLeft() != r2.topLeft()
- || r1.size() != r2.size();
+ return qFuzzyCompare(lhs.topLeft(), rhs.topLeft())
+ && qFuzzyCompare(lhs.bottomRight(), rhs.bottomRight());
}
+ friend constexpr bool qFuzzyIsNull(const QRectF &rect) noexcept
+ {
+ return qFuzzyIsNull(rect.w) && qFuzzyIsNull(rect.h);
+ }
+
+public:
[[nodiscard]] constexpr inline QRect toRect() const noexcept;
[[nodiscard]] QRect toAlignedRect() const noexcept;
diff --git a/src/corelib/tools/qringbuffer.cpp b/src/corelib/tools/qringbuffer.cpp
index ea4224eefe..0645759118 100644
--- a/src/corelib/tools/qringbuffer.cpp
+++ b/src/corelib/tools/qringbuffer.cpp
@@ -3,7 +3,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "private/qringbuffer_p.h"
-#include "private/qbytearray_p.h"
#include <type_traits>
@@ -91,7 +90,7 @@ void QRingBuffer::free(qint64 bytes)
clear(); // try to minify/squeeze us
}
} else {
- Q_ASSERT(bytes < MaxByteArraySize);
+ Q_ASSERT(bytes < QByteArray::max_size());
chunk.advance(bytes);
bufferSize -= bytes;
}
@@ -106,7 +105,7 @@ void QRingBuffer::free(qint64 bytes)
char *QRingBuffer::reserve(qint64 bytes)
{
- Q_ASSERT(bytes > 0 && bytes < MaxByteArraySize);
+ Q_ASSERT(bytes > 0 && bytes < QByteArray::max_size());
const qsizetype chunkSize = qMax(qint64(basicBlockSize), bytes);
qsizetype tail = 0;
@@ -136,7 +135,7 @@ char *QRingBuffer::reserve(qint64 bytes)
*/
char *QRingBuffer::reserveFront(qint64 bytes)
{
- Q_ASSERT(bytes > 0 && bytes < MaxByteArraySize);
+ Q_ASSERT(bytes > 0 && bytes < QByteArray::max_size());
const qsizetype chunkSize = qMax(qint64(basicBlockSize), bytes);
if (bufferSize == 0) {
@@ -182,7 +181,7 @@ void QRingBuffer::chop(qint64 bytes)
clear(); // try to minify/squeeze us
}
} else {
- Q_ASSERT(bytes < MaxByteArraySize);
+ Q_ASSERT(bytes < QByteArray::max_size());
chunk.grow(-bytes);
bufferSize -= bytes;
}
diff --git a/src/corelib/tools/qscopedpointer.cpp b/src/corelib/tools/qscopedpointer.cpp
index 28cc39ae5d..515eb9dc75 100644
--- a/src/corelib/tools/qscopedpointer.cpp
+++ b/src/corelib/tools/qscopedpointer.cpp
@@ -269,7 +269,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn template <typename T, typename Cleanup> template <typename D> QScopedArrayPointer<T, Cleanup>::QScopedArrayPointer(D * p)
+ \fn template <typename T, typename Cleanup> template <typename D, QScopedArrayPointer<T, Cleanup>::if_same_type<D> = true> QScopedArrayPointer<T, Cleanup>::QScopedArrayPointer(D * p)
Constructs a QScopedArrayPointer and stores the array of objects
pointed to by \a p.
diff --git a/src/corelib/tools/qscopedpointer.h b/src/corelib/tools/qscopedpointer.h
index 1637bb40a5..59bae9b967 100644
--- a/src/corelib/tools/qscopedpointer.h
+++ b/src/corelib/tools/qscopedpointer.h
@@ -67,9 +67,10 @@ typedef QScopedPointerObjectDeleteLater<QObject> QScopedPointerDeleteLater;
#endif
template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
-class [[nodiscard]] QScopedPointer
+class QScopedPointer
{
public:
+ Q_NODISCARD_CTOR
explicit QScopedPointer(T *p = nullptr) noexcept : d(p)
{
}
@@ -187,15 +188,17 @@ private:
};
template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
-class [[nodiscard]] QScopedArrayPointer : public QScopedPointer<T, Cleanup>
+class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
{
template <typename Ptr>
using if_same_type = typename std::enable_if<std::is_same<typename std::remove_cv<T>::type, Ptr>::value, bool>::type;
public:
+ Q_NODISCARD_CTOR
inline QScopedArrayPointer() : QScopedPointer<T, Cleanup>(nullptr) {}
inline ~QScopedArrayPointer() = default;
template <typename D, if_same_type<D> = true>
+ Q_NODISCARD_CTOR
explicit QScopedArrayPointer(D *p)
: QScopedPointer<T, Cleanup>(p)
{
diff --git a/src/corelib/tools/qscopedvaluerollback.h b/src/corelib/tools/qscopedvaluerollback.h
index 53a31e4675..0ae3efd0c0 100644
--- a/src/corelib/tools/qscopedvaluerollback.h
+++ b/src/corelib/tools/qscopedvaluerollback.h
@@ -9,14 +9,16 @@
QT_BEGIN_NAMESPACE
template <typename T>
-class [[nodiscard]] QScopedValueRollback
+class QScopedValueRollback
{
public:
+ Q_NODISCARD_CTOR
explicit constexpr QScopedValueRollback(T &var)
: varRef(var), oldValue(var)
{
}
+ Q_NODISCARD_CTOR
explicit constexpr QScopedValueRollback(T &var, T value)
: varRef(var), oldValue(std::move(var)) // ### C++20: std::exchange(var, std::move(value))
{
diff --git a/src/corelib/tools/qscopeguard.h b/src/corelib/tools/qscopeguard.h
index 5bd202ce33..9be6634cc8 100644
--- a/src/corelib/tools/qscopeguard.h
+++ b/src/corelib/tools/qscopeguard.h
@@ -13,19 +13,22 @@
QT_BEGIN_NAMESPACE
template <typename F>
-class [[nodiscard]] QScopeGuard
+class QScopeGuard
{
public:
+ Q_NODISCARD_CTOR
explicit QScopeGuard(F &&f) noexcept
: m_func(std::move(f))
{
}
+ Q_NODISCARD_CTOR
explicit QScopeGuard(const F &f) noexcept
: m_func(f)
{
}
+ Q_NODISCARD_CTOR
QScopeGuard(QScopeGuard &&other) noexcept
: m_func(std::move(other.m_func))
, m_invoke(std::exchange(other.m_invoke, false))
diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h
index cdd9ceda20..6eaeb8fc41 100644
--- a/src/corelib/tools/qset.h
+++ b/src/corelib/tools/qset.h
@@ -6,6 +6,7 @@
#include <QtCore/qhash.h>
#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/qttypetraits.h>
#include <initializer_list>
#include <iterator>
@@ -189,14 +190,18 @@ public:
inline QSet<T> &operator+=(const T &value) { insert(value); return *this; }
inline QSet<T> &operator-=(const QSet<T> &other) { subtract(other); return *this; }
inline QSet<T> &operator-=(const T &value) { remove(value); return *this; }
- inline QSet<T> operator|(const QSet<T> &other) const
- { QSet<T> result = *this; result |= other; return result; }
- inline QSet<T> operator&(const QSet<T> &other) const
- { QSet<T> result = *this; result &= other; return result; }
- inline QSet<T> operator+(const QSet<T> &other) const
- { QSet<T> result = *this; result += other; return result; }
- inline QSet<T> operator-(const QSet<T> &other) const
- { QSet<T> result = *this; result -= other; return result; }
+
+ friend QSet operator|(const QSet &lhs, const QSet &rhs) { return QSet(lhs) |= rhs; }
+ friend QSet operator|(QSet &&lhs, const QSet &rhs) { lhs |= rhs; return std::move(lhs); }
+
+ friend QSet operator&(const QSet &lhs, const QSet &rhs) { return QSet(lhs) &= rhs; }
+ friend QSet operator&(QSet &&lhs, const QSet &rhs) { lhs &= rhs; return std::move(lhs); }
+
+ friend QSet operator+(const QSet &lhs, const QSet &rhs) { return QSet(lhs) += rhs; }
+ friend QSet operator+(QSet &&lhs, const QSet &rhs) { lhs += rhs; return std::move(lhs); }
+
+ friend QSet operator-(const QSet &lhs, const QSet &rhs) { return QSet(lhs) -= rhs; }
+ friend QSet operator-(QSet &&lhs, const QSet &rhs) { lhs -= rhs; return std::move(lhs); }
QList<T> values() const;
@@ -224,10 +229,13 @@ Q_INLINE_TEMPLATE void QSet<T>::reserve(qsizetype asize) { q_hash.reserve(asize)
template <class T>
Q_INLINE_TEMPLATE QSet<T> &QSet<T>::unite(const QSet<T> &other)
{
- if (!q_hash.isSharedWith(other.q_hash)) {
- for (const T &e : other)
- insert(e);
- }
+ if (q_hash.isSharedWith(other.q_hash))
+ return *this;
+ QSet<T> tmp = other;
+ if (size() < other.size())
+ swap(tmp);
+ for (const auto &e : std::as_const(tmp))
+ insert(e);
return *this;
}
diff --git a/src/corelib/tools/qset.qdoc b/src/corelib/tools/qset.qdoc
index 31b431e43f..4ef7a80a52 100644
--- a/src/corelib/tools/qset.qdoc
+++ b/src/corelib/tools/qset.qdoc
@@ -86,7 +86,7 @@
initializer list \a list.
*/
-/*! \fn template <class T> template<typename InputIterator> QSet<T>::QSet(InputIterator first, InputIterator last)
+/*! \fn template <class T> template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true> QSet<T>::QSet(InputIterator first, InputIterator last)
\since 5.14
Constructs a set with the contents in the iterator range [\a first, \a last).
@@ -567,29 +567,30 @@
*/
/*!
- \fn template <class T> QSet<T> QSet<T>::operator|(const QSet<T> &other) const
- \fn template <class T> QSet<T> QSet<T>::operator+(const QSet<T> &other) const
+ \fn template <class T> QSet<T> QSet<T>::operator|(const QSet &lhs, const QSet &rhs)
+ \fn template <class T> QSet<T> QSet<T>::operator|(QSet &&lhs, const QSet &rhs)
+ \fn template <class T> QSet<T> QSet<T>::operator+(const QSet &lhs, const QSet &rhs)
+ \fn template <class T> QSet<T> QSet<T>::operator+(QSet &&lhs, const QSet &rhs)
- Returns a new QSet that is the union of this set and the
- \a other set.
+ Returns a new QSet that is the union of sets \a lhs and \a rhs.
\sa unite(), operator|=(), operator&(), operator-()
*/
/*!
- \fn template <class T> QSet<T> QSet<T>::operator&(const QSet<T> &other) const
+ \fn template <class T> QSet<T> QSet<T>::operator&(const QSet &lhs, const QSet &rhs)
+ \fn template <class T> QSet<T> QSet<T>::operator&(QSet &&lhs, const QSet &rhs)
- Returns a new QSet that is the intersection of this set and the
- \a other set.
+ Returns a new QSet that is the intersection of sets \a lhs and \a rhs.
\sa intersect(), operator&=(), operator|(), operator-()
*/
/*!
- \fn template <class T> QSet<T> QSet<T>::operator-(const QSet<T> &other) const
+ \fn template <class T> QSet<T> QSet<T>::operator-(const QSet &lhs, const QSet &rhs)
+ \fn template <class T> QSet<T> QSet<T>::operator-(QSet &&lhs, const QSet &rhs)
- Returns a new QSet that is the set difference of this set and
- the \a other set, i.e., this set - \a other set.
+ Returns a new QSet that is the set difference of sets \a lhs and \a rhs.
\sa subtract(), operator-=(), operator|(), operator&()
*/
@@ -886,3 +887,10 @@
from the set \a set. Returns the number of elements removed, if
any.
*/
+
+/*! \fn template <class T> template <class Pred> qsizetype QSet<T>::removeIf(Pred pred)
+ \since 6.1
+
+ Removes, from this set, all elements for which the predicate \a pred
+ returns \c true. Returns the number of elements removed, if any.
+*/
diff --git a/src/corelib/tools/qshareddata.h b/src/corelib/tools/qshareddata.h
index 826152b63a..4c4153a506 100644
--- a/src/corelib/tools/qshareddata.h
+++ b/src/corelib/tools/qshareddata.h
@@ -51,13 +51,17 @@ public:
const T *constData() const noexcept { return d; }
T *take() noexcept { return std::exchange(d, nullptr); }
+ Q_NODISCARD_CTOR
QSharedDataPointer() noexcept : d(nullptr) { }
~QSharedDataPointer() { if (d && !d->ref.deref()) delete d; }
+ Q_NODISCARD_CTOR
explicit QSharedDataPointer(T *data) noexcept : d(data)
{ if (d) d->ref.ref(); }
+ Q_NODISCARD_CTOR
QSharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data)
{}
+ Q_NODISCARD_CTOR
QSharedDataPointer(const QSharedDataPointer &o) noexcept : d(o.d)
{ if (d) d->ref.ref(); }
@@ -82,6 +86,7 @@ public:
reset(o);
return *this;
}
+ Q_NODISCARD_CTOR
QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer)
@@ -139,17 +144,22 @@ public:
void detach() { if (d && d->ref.loadRelaxed() != 1) detach_helper(); }
+ Q_NODISCARD_CTOR
QExplicitlySharedDataPointer() noexcept : d(nullptr) { }
~QExplicitlySharedDataPointer() { if (d && !d->ref.deref()) delete d; }
+ Q_NODISCARD_CTOR
explicit QExplicitlySharedDataPointer(T *data) noexcept : d(data)
{ if (d) d->ref.ref(); }
+ Q_NODISCARD_CTOR
QExplicitlySharedDataPointer(T *data, QAdoptSharedDataTag) noexcept : d(data)
{}
+ Q_NODISCARD_CTOR
QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer &o) noexcept : d(o.d)
{ if (d) d->ref.ref(); }
template<typename X>
+ Q_NODISCARD_CTOR
QExplicitlySharedDataPointer(const QExplicitlySharedDataPointer<X> &o) noexcept
#ifdef QT_ENABLE_QEXPLICITLYSHAREDDATAPOINTER_STATICCAST
: d(static_cast<T *>(o.data()))
@@ -179,6 +189,7 @@ public:
reset(o);
return *this;
}
+ Q_NODISCARD_CTOR
QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer)
diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp
index 7ed3847182..217a3a4ff4 100644
--- a/src/corelib/tools/qsharedpointer.cpp
+++ b/src/corelib/tools/qsharedpointer.cpp
@@ -139,8 +139,7 @@
can also exceptionally be -1, indicating that there are no QSharedPointers
attached to an object, which is tracked too. The only case where this is
possible is that of QWeakPointers and QPointers tracking a QObject. Note
- that QWeakPointers tracking a QObject is a deprecated feature as of Qt 5.0,
- kept only for compatibility with Qt 4.x.
+ that QWeakPointers tracking a QObject is deprecated.
The weak reference count controls the lifetime of the d-pointer itself.
It can be thought of as an internal/intrusive reference count for
@@ -175,7 +174,7 @@
last QSharedPointer instance had.
This class is never instantiated directly: the constructors and
- destructor are private and, in C++11, deleted. Only the create() function
+ destructor are deleted. Only the create() function
may be called to return an object of this type. See below for construction
details.
@@ -214,8 +213,7 @@
Like ExternalRefCountWithCustomDeleter, this class is never instantiated
directly. This class also provides a create() member that returns the
- pointer, and hides its constructors and destructor. With C++11, they're
- deleted.
+ pointer, and deletes its constructors and destructor.
The size of this class depends on the size of \tt T.
@@ -711,6 +709,49 @@
*/
/*!
+ \fn template <class T> template <class X> bool QSharedPointer<T>::owner_before(const QSharedPointer<X> &other) const noexcept
+ \fn template <class T> template <class X> bool QSharedPointer<T>::owner_before(const QWeakPointer<X> &other) const noexcept
+ \fn template <class T> template <class X> bool QWeakPointer<T>::owner_before(const QSharedPointer<X> &other) const noexcept
+ \fn template <class T> template <class X> bool QWeakPointer<T>::owner_before(const QWeakPointer<X> &other) const noexcept
+ \since 6.7
+
+ Returns \c true if and only if this smart pointer precedes \a other
+ in an implementation-defined owner-based ordering. The ordering is such
+ that two smart pointers are considered equivalent if they are both
+ empty or if they both own the same object (even if their apparent type
+ and pointer are different).
+
+ \sa owner_equal
+*/
+
+/*!
+ \fn template <class T> template <class X> bool QSharedPointer<T>::owner_equal(const QSharedPointer<X> &other) const noexcept
+ \fn template <class T> template <class X> bool QSharedPointer<T>::owner_equal(const QWeakPointer<X> &other) const noexcept
+ \fn template <class T> template <class X> bool QWeakPointer<T>::owner_equal(const QSharedPointer<X> &other) const noexcept
+ \fn template <class T> template <class X> bool QWeakPointer<T>::owner_equal(const QWeakPointer<X> &other) const noexcept
+
+ \since 6.7
+
+ Returns \c true if and only if this smart pointer and \a other
+ share ownership.
+
+ \sa owner_before, owner_hash
+*/
+
+/*!
+ \fn template <class T> size_t QSharedPointer<T>::owner_hash() const noexcept
+ \fn template <class T> size_t QWeakPointer<T>::owner_hash() const noexcept
+
+ \since 6.7
+
+ Returns a owner-based hash value for this smart pointer object.
+ Smart pointers that compare equal (as per \c{owner_equal}) will
+ have an identical owner-based hash.
+
+ \sa owner_equal
+*/
+
+/*!
\fn template <class T> QWeakPointer<T>::QWeakPointer()
Creates a QWeakPointer that points to nothing.
@@ -945,7 +986,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
\relates QSharedPointer
Returns \c true if \a ptr1 and \a ptr2 refer to the same pointer.
@@ -958,7 +999,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator!=(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator!=(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
\relates QSharedPointer
Returns \c true if \a ptr1 and \a ptr2 refer to distinct pointers.
@@ -971,7 +1012,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator==(const QSharedPointer<T> &ptr1, const X *ptr2)
+ \fn template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const X *ptr2)
\relates QSharedPointer
Returns \c true if \a ptr1 and \a ptr2 refer to the same pointer.
@@ -984,7 +1025,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator!=(const QSharedPointer<T> &ptr1, const X *ptr2)
+ \fn template<class T, class X> bool operator!=(const QSharedPointer<T> &ptr1, const X *ptr2)
\relates QSharedPointer
Returns \c true if \a ptr1 and \a ptr2 refer to distinct pointers.
@@ -997,7 +1038,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator==(const T *ptr1, const QSharedPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator==(const T *ptr1, const QSharedPointer<X> &ptr2)
\relates QSharedPointer
Returns \c true if the pointer \a ptr1 is the
@@ -1011,7 +1052,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator!=(const T *ptr1, const QSharedPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator!=(const T *ptr1, const QSharedPointer<X> &ptr2)
\relates QSharedPointer
Returns \c true if the pointer \a ptr1 is not the
@@ -1025,7 +1066,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator==(const QSharedPointer<T> &ptr1, const QWeakPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator==(const QSharedPointer<T> &ptr1, const QWeakPointer<X> &ptr2)
\relates QWeakPointer
Returns \c true if \a ptr1 and \a ptr2 refer to the same pointer.
@@ -1038,7 +1079,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator!=(const QSharedPointer<T> &ptr1, const QWeakPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator!=(const QSharedPointer<T> &ptr1, const QWeakPointer<X> &ptr2)
\relates QWeakPointer
Returns \c true if \a ptr1 and \a ptr2 refer to distinct pointers.
@@ -1051,7 +1092,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator==(const QWeakPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator==(const QWeakPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
\relates QWeakPointer
Returns \c true if \a ptr1 and \a ptr2 refer to the same pointer.
@@ -1144,7 +1185,7 @@
*/
/*!
- \fn template <class T> template <class X> bool operator!=(const QWeakPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
+ \fn template<class T, class X> bool operator!=(const QWeakPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
\relates QWeakPointer
Returns \c true if \a ptr1 and \a ptr2 refer to distinct pointers.
@@ -1157,7 +1198,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerCast(const QSharedPointer<T> &other)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerCast(const QSharedPointer<T> &other)
\relates QSharedPointer
Returns a shared pointer to the pointer held by \a other, cast to
@@ -1172,7 +1213,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerCast(const QWeakPointer<T> &other)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerCast(const QWeakPointer<T> &other)
\relates QSharedPointer
\relates QWeakPointer
@@ -1193,7 +1234,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerDynamicCast(const QSharedPointer<T> &src)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerDynamicCast(const QSharedPointer<T> &src)
\relates QSharedPointer
Returns a shared pointer to the pointer held by \a src, using a
@@ -1209,7 +1250,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerDynamicCast(const QWeakPointer<T> &src)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerDynamicCast(const QWeakPointer<T> &src)
\relates QSharedPointer
\relates QWeakPointer
@@ -1231,7 +1272,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerConstCast(const QSharedPointer<T> &src)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerConstCast(const QSharedPointer<T> &src)
\relates QSharedPointer
Returns a shared pointer to the pointer held by \a src, cast to
@@ -1243,7 +1284,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerConstCast(const QWeakPointer<T> &src)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerConstCast(const QWeakPointer<T> &src)
\relates QSharedPointer
\relates QWeakPointer
@@ -1261,7 +1302,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerObjectCast(const QSharedPointer<T> &src)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerObjectCast(const QSharedPointer<T> &src)
\relates QSharedPointer
\since 4.6
@@ -1333,7 +1374,7 @@
*/
/*!
- \fn template <class X> template <class T> QSharedPointer<X> qSharedPointerObjectCast(const QWeakPointer<T> &src)
+ \fn template <class X, class T> QSharedPointer<X> qSharedPointerObjectCast(const QWeakPointer<T> &src)
\relates QSharedPointer
\relates QWeakPointer
\since 4.6
@@ -1359,7 +1400,7 @@
/*!
- \fn template <class X> template <class T> QWeakPointer<X> qWeakPointerCast(const QWeakPointer<T> &src)
+ \fn template <class X, class T> QWeakPointer<X> qWeakPointerCast(const QWeakPointer<T> &src)
\relates QWeakPointer
Returns a weak pointer to the pointer held by \a src, cast to
@@ -1379,6 +1420,7 @@
QT_BEGIN_NAMESPACE
+QT6_ONLY(
/*!
\internal
This function is called for a just-created QObject \a obj, to enable
@@ -1386,7 +1428,9 @@ QT_BEGIN_NAMESPACE
*/
void QtSharedPointer::ExternalRefCountData::setQObjectShared(const QObject *, bool)
{}
+)
+QT6_ONLY(
/*!
\internal
This function is called when a QSharedPointer is created from a QWeakPointer
@@ -1399,6 +1443,7 @@ void QtSharedPointer::ExternalRefCountData::checkQObjectShared(const QObject *)
if (strongref.loadRelaxed() < 0)
qWarning("QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer");
}
+)
QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::getAndRef(const QObject *obj)
{
diff --git a/src/corelib/tools/qsharedpointer.h b/src/corelib/tools/qsharedpointer.h
index 2a60f3ca5e..116c9afa00 100644
--- a/src/corelib/tools/qsharedpointer.h
+++ b/src/corelib/tools/qsharedpointer.h
@@ -72,6 +72,19 @@ public:
template <typename... Args>
static inline QSharedPointer<T> create(Args &&... args);
+
+ // owner-based comparisons
+ template <typename X>
+ bool owner_before(const QSharedPointer<X> &other) const noexcept;
+ template <typename X>
+ bool owner_before(const QWeakPointer<X> &other) const noexcept;
+
+ template <typename X>
+ bool owner_equal(const QSharedPointer<X> &other) const noexcept;
+ template <typename X>
+ bool owner_equal(const QWeakPointer<X> &other) const noexcept;
+
+ size_t owner_hash() const noexcept;
};
template <class T>
@@ -108,6 +121,19 @@ public:
QSharedPointer<T> toStrongRef() const;
QSharedPointer<T> lock() const;
+
+ // owner-based comparisons
+ template <typename X>
+ bool owner_before(const QWeakPointer<X> &other) const noexcept;
+ template <typename X>
+ bool owner_before(const QSharedPointer<X> &other) const noexcept;
+
+ template <typename X>
+ bool owner_equal(const QWeakPointer<X> &other) const noexcept;
+ template <typename X>
+ bool owner_equal(const QSharedPointer<X> &other) const noexcept;
+
+ size_t owner_hash() const noexcept;
};
template <class T>
diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h
index dd6bd22fca..456be91d03 100644
--- a/src/corelib/tools/qsharedpointer_impl.h
+++ b/src/corelib/tools/qsharedpointer_impl.h
@@ -28,6 +28,7 @@ QT_END_NAMESPACE
#include <QtCore/qatomic.h>
#include <QtCore/qhashfunctions.h>
#include <QtCore/qmetatype.h> // for IsPointerToTypeDerivedFromQObject
+#include <QtCore/qxptype_traits.h>
#include <memory>
@@ -115,8 +116,10 @@ namespace QtSharedPointer {
#ifndef QT_NO_QOBJECT
Q_CORE_EXPORT static ExternalRefCountData *getAndRef(const QObject *);
+ QT6_ONLY(
Q_CORE_EXPORT void setQObjectShared(const QObject *, bool enable);
- Q_CORE_EXPORT void checkQObjectShared(const QObject *);
+ )
+ QT6_ONLY(Q_CORE_EXPORT void checkQObjectShared(const QObject *);)
#endif
inline void checkQObjectShared(...) { }
inline void setQObjectShared(...) { }
@@ -276,23 +279,29 @@ public:
T &operator*() const { return *data(); }
T *operator->() const noexcept { return data(); }
+ Q_NODISCARD_CTOR
constexpr QSharedPointer() noexcept : value(nullptr), d(nullptr) { }
~QSharedPointer() { deref(); }
+ Q_NODISCARD_CTOR
constexpr QSharedPointer(std::nullptr_t) noexcept : value(nullptr), d(nullptr) { }
template <class X, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
inline explicit QSharedPointer(X *ptr) : value(ptr) // noexcept
{ internalConstruct(ptr, QtSharedPointer::NormalDeleter()); }
template <class X, typename Deleter, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
inline QSharedPointer(X *ptr, Deleter deleter) : value(ptr) // throws
{ internalConstruct(ptr, deleter); }
template <typename Deleter>
+ Q_NODISCARD_CTOR
QSharedPointer(std::nullptr_t, Deleter deleter) : value(nullptr)
{ internalConstruct(static_cast<T *>(nullptr), deleter); }
+ Q_NODISCARD_CTOR
QSharedPointer(const QSharedPointer &other) noexcept : value(other.value), d(other.d)
{ if (d) ref(); }
QSharedPointer &operator=(const QSharedPointer &other) noexcept
@@ -301,6 +310,7 @@ public:
swap(copy);
return *this;
}
+ Q_NODISCARD_CTOR
QSharedPointer(QSharedPointer &&other) noexcept
: value(other.value), d(other.d)
{
@@ -310,6 +320,7 @@ public:
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedPointer)
template <class X, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
QSharedPointer(QSharedPointer<X> &&other) noexcept
: value(other.value), d(other.d)
{
@@ -326,6 +337,7 @@ public:
}
template <class X, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
QSharedPointer(const QSharedPointer<X> &other) noexcept : value(other.value), d(other.d)
{ if (d) ref(); }
@@ -338,6 +350,7 @@ public:
}
template <class X, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
inline QSharedPointer(const QWeakPointer<X> &other) : value(nullptr), d(nullptr)
{ *this = other; }
@@ -383,10 +396,10 @@ public:
inline void clear() { QSharedPointer copy; swap(copy); }
- QWeakPointer<T> toWeakRef() const;
+ [[nodiscard]] QWeakPointer<T> toWeakRef() const;
template <typename... Args>
- static QSharedPointer create(Args && ...arguments)
+ [[nodiscard]] static QSharedPointer create(Args && ...arguments)
{
typedef QtSharedPointer::ExternalRefCountWithContiguousData<T> Private;
# ifdef QT_SHAREDPOINTER_TRACK_POINTERS
@@ -433,7 +446,25 @@ public:
#undef DECLARE_TEMPLATE_COMPARE_SET
#undef DECLARE_COMPARE_SET
+ template <typename X>
+ bool owner_before(const QSharedPointer<X> &other) const noexcept
+ { return std::less<>()(d, other.d); }
+ template <typename X>
+ bool owner_before(const QWeakPointer<X> &other) const noexcept
+ { return std::less<>()(d, other.d); }
+
+ template <typename X>
+ bool owner_equal(const QSharedPointer<X> &other) const noexcept
+ { return d == other.d; }
+ template <typename X>
+ bool owner_equal(const QWeakPointer<X> &other) const noexcept
+ { return d == other.d; }
+
+ size_t owner_hash() const noexcept
+ { return std::hash<Data *>()(d); }
+
private:
+ Q_NODISCARD_CTOR
explicit QSharedPointer(Qt::Initialization) {}
void deref() noexcept
@@ -470,7 +501,6 @@ private:
#ifdef QT_SHAREDPOINTER_TRACK_POINTERS
internalSafetyCheckAdd(d, ptr);
#endif
- d->setQObjectShared(ptr, true);
enableSharedFromThis(ptr);
}
@@ -498,12 +528,10 @@ private:
tmp = o->strongref.loadRelaxed(); // failed, try again
}
- if (tmp > 0) {
+ if (tmp > 0)
o->weakref.ref();
- } else {
- o->checkQObjectShared(actual);
+ else
o = nullptr;
- }
}
qt_ptr_swap(d, o);
@@ -526,6 +554,12 @@ class QWeakPointer
template <typename X>
using IfCompatible = typename std::enable_if<std::is_convertible<X*, T*>::value, bool>::type;
+ template <typename X>
+ using IfVirtualBase = typename std::enable_if<qxp::is_virtual_base_of_v<T, X>, bool>::type;
+
+ template <typename X>
+ using IfNotVirtualBase = typename std::enable_if<!qxp::is_virtual_base_of_v<T, X>, bool>::type;
+
public:
typedef T element_type;
typedef T value_type;
@@ -539,11 +573,14 @@ public:
explicit operator bool() const noexcept { return !isNull(); }
bool operator !() const noexcept { return isNull(); }
+ Q_NODISCARD_CTOR
constexpr QWeakPointer() noexcept : d(nullptr), value(nullptr) { }
inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }
+ Q_NODISCARD_CTOR
QWeakPointer(const QWeakPointer &other) noexcept : d(other.d), value(other.value)
{ if (d) d->weakref.ref(); }
+ Q_NODISCARD_CTOR
QWeakPointer(QWeakPointer &&other) noexcept
: d(other.d), value(other.value)
{
@@ -552,9 +589,18 @@ public:
}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QWeakPointer)
- template <class X, IfCompatible<X> = true>
+ template <class X, IfCompatible<X> = true, IfNotVirtualBase<X> = true>
+ Q_NODISCARD_CTOR
QWeakPointer(QWeakPointer<X> &&other) noexcept
- : d(other.d), value(other.value)
+ : d(std::exchange(other.d, nullptr)),
+ value(std::exchange(other.value, nullptr))
+ {
+ }
+
+ template <class X, IfCompatible<X> = true, IfVirtualBase<X> = true>
+ Q_NODISCARD_CTOR
+ QWeakPointer(QWeakPointer<X> &&other) noexcept
+ : d(other.d), value(other.toStrongRef().get()) // must go through QSharedPointer, see below
{
other.d = nullptr;
other.value = nullptr;
@@ -581,6 +627,7 @@ public:
qt_ptr_swap(this->value, other.value);
}
+ Q_NODISCARD_CTOR
inline QWeakPointer(const QSharedPointer<T> &o) : d(o.d), value(o.data())
{ if (d) d->weakref.ref();}
inline QWeakPointer &operator=(const QSharedPointer<T> &o)
@@ -590,6 +637,7 @@ public:
}
template <class X, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
inline QWeakPointer(const QWeakPointer<X> &o) : d(nullptr), value(nullptr)
{ *this = o; }
@@ -603,6 +651,7 @@ public:
}
template <class X, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
inline QWeakPointer(const QSharedPointer<X> &o) : d(nullptr), value(nullptr)
{ *this = o; }
@@ -615,9 +664,9 @@ public:
inline void clear() { *this = QWeakPointer(); }
- inline QSharedPointer<T> toStrongRef() const { return QSharedPointer<T>(*this); }
+ [[nodiscard]] QSharedPointer<T> toStrongRef() const { return QSharedPointer<T>(*this); }
// std::weak_ptr compatibility:
- inline QSharedPointer<T> lock() const { return toStrongRef(); }
+ [[nodiscard]] QSharedPointer<T> lock() const { return toStrongRef(); }
template <class X>
bool operator==(const QWeakPointer<X> &o) const noexcept
@@ -651,6 +700,23 @@ public:
friend bool operator!=(std::nullptr_t, const QWeakPointer &p)
{ return !p.isNull(); }
+ template <typename X>
+ bool owner_before(const QWeakPointer<X> &other) const noexcept
+ { return std::less<>()(d, other.d); }
+ template <typename X>
+ bool owner_before(const QSharedPointer<X> &other) const noexcept
+ { return std::less<>()(d, other.d); }
+
+ template <typename X>
+ bool owner_equal(const QWeakPointer<X> &other) const noexcept
+ { return d == other.d; }
+ template <typename X>
+ bool owner_equal(const QSharedPointer<X> &other) const noexcept
+ { return d == other.d; }
+
+ size_t owner_hash() const noexcept
+ { return std::hash<Data *>()(d); }
+
private:
friend struct QtPrivate::EnableInternalData;
template <class X> friend class QSharedPointer;
@@ -659,10 +725,11 @@ private:
template <class X>
inline QWeakPointer &assign(X *ptr)
- { return *this = QWeakPointer<X>(ptr, true); }
+ { return *this = QWeakPointer<T>(ptr, true); }
#ifndef QT_NO_QOBJECT
template <class X, IfCompatible<X> = true>
+ Q_NODISCARD_CTOR
inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
{ }
#endif
diff --git a/src/corelib/tools/qsize.cpp b/src/corelib/tools/qsize.cpp
index d5e8e4c71b..27ff1d164d 100644
--- a/src/corelib/tools/qsize.cpp
+++ b/src/corelib/tools/qsize.cpp
@@ -266,15 +266,15 @@ QSize QSize::scaled(const QSize &s, Qt::AspectRatioMode mode) const noexcept
*/
/*!
- \fn bool QSize::operator==(const QSize &s1, const QSize &s2)
+ \fn bool QSize::operator==(const QSize &lhs, const QSize &rhs)
- Returns \c true if \a s1 and \a s2 are equal; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
*/
/*!
- \fn bool QSize::operator!=(const QSize &s1, const QSize &s2)
+ \fn bool QSize::operator!=(const QSize &lhs, const QSize &rhs)
- Returns \c true if \a s1 and \a s2 are different; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are different; otherwise returns \c false.
*/
/*!
@@ -714,9 +714,9 @@ QSizeF QSizeF::scaled(const QSizeF &s, Qt::AspectRatioMode mode) const noexcept
*/
/*!
- \fn bool QSizeF::operator==(const QSizeF &s1, const QSizeF &s2)
+ \fn bool QSizeF::operator==(const QSizeF &lhs, const QSizeF &rhs)
- Returns \c true if \a s1 and \a s2 are approximately equal; otherwise
+ Returns \c true if \a lhs and \a rhs are approximately equal; otherwise
returns false.
\warning This function does not check for strict equality; instead,
@@ -726,9 +726,9 @@ QSizeF QSizeF::scaled(const QSizeF &s, Qt::AspectRatioMode mode) const noexcept
*/
/*!
- \fn bool QSizeF::operator!=(const QSizeF &s1, const QSizeF &s2)
+ \fn bool QSizeF::operator!=(const QSizeF &lhs, const QSizeF &rhs)
- Returns \c true if \a s1 and \a s2 are sufficiently different; otherwise
+ Returns \c true if \a lhs and \a rhs are sufficiently different; otherwise
returns \c false.
\warning This function does not check for strict inequality; instead,
@@ -808,7 +808,24 @@ QSizeF QSizeF::scaled(const QSizeF &s, Qt::AspectRatioMode mode) const noexcept
\sa expandedTo(), scale()
*/
+/*!
+ \fn bool QSizeF::qFuzzyCompare(const QSizeF &lhs, const QSizeF &rhs)
+ \since 6.8
+
+ Returns \c true if the size \a lhs is approximately equal to the
+ size \a rhs; otherwise returns \c false.
+
+ The sizes are considered approximately equal if their width and
+ height are approximately equal.
+*/
+/*!
+ \fn bool QSizeF::qFuzzyIsNull(const QSizeF &size)
+ \since 6.8
+
+ Returns \c true if both width and height of the size \a size
+ are approximately equal to zero.
+*/
/*****************************************************************************
QSizeF stream functions
diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h
index a5eaf34afe..67f7146201 100644
--- a/src/corelib/tools/qsize.h
+++ b/src/corelib/tools/qsize.h
@@ -59,10 +59,10 @@ public:
constexpr inline QSize &operator*=(qreal c) noexcept;
inline QSize &operator/=(qreal c);
- friend inline constexpr bool operator==(const QSize &s1, const QSize &s2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QSize &s1, const QSize &s2) noexcept
{ return s1.wd == s2.wd && s1.ht == s2.ht; }
- friend inline constexpr bool operator!=(const QSize &s1, const QSize &s2) noexcept
- { return s1.wd != s2.wd || s1.ht != s2.ht; }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSize)
friend inline constexpr QSize operator+(const QSize &s1, const QSize &s2) noexcept
{ return QSize(s1.wd + s2.wd, s1.ht + s2.ht); }
friend inline constexpr QSize operator-(const QSize &s1, const QSize &s2) noexcept
@@ -75,6 +75,7 @@ public:
{ Q_ASSERT(!qFuzzyIsNull(c)); return QSize(qRound(s.wd / c), qRound(s.ht / c)); }
friend inline constexpr size_t qHash(const QSize &, size_t) noexcept;
+public:
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
[[nodiscard]] CGSize toCGSize() const noexcept;
#endif
@@ -242,16 +243,25 @@ public:
constexpr inline QSizeF &operator*=(qreal c) noexcept;
inline QSizeF &operator/=(qreal c);
+private:
QT_WARNING_PUSH
QT_WARNING_DISABLE_FLOAT_COMPARE
- friend constexpr inline bool operator==(const QSizeF &s1, const QSizeF &s2)
+ 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));
}
QT_WARNING_POP
- friend constexpr inline bool operator!=(const QSizeF &s1, const QSizeF &s2)
- { return !(s1 == s2); }
+ friend constexpr bool qFuzzyIsNull(const QSizeF &size) noexcept
+ { return qFuzzyIsNull(size.wd) && qFuzzyIsNull(size.ht); }
+ friend constexpr bool comparesEqual(const QSizeF &lhs, const QSizeF &rhs) noexcept
+ { return qFuzzyCompare(lhs, rhs); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSizeF)
+ friend constexpr bool comparesEqual(const QSizeF &lhs, const QSize &rhs) noexcept
+ { return comparesEqual(lhs, rhs.toSizeF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSizeF, QSize)
friend constexpr inline QSizeF operator+(const QSizeF &s1, const QSizeF &s2) noexcept
{ return QSizeF(s1.wd + s2.wd, s1.ht + s2.ht); }
friend constexpr inline QSizeF operator-(const QSizeF &s1, const QSizeF &s2) noexcept
@@ -263,6 +273,7 @@ public:
friend inline QSizeF operator/(const QSizeF &s, qreal c)
{ Q_ASSERT(!qFuzzyIsNull(c)); return QSizeF(s.wd / c, s.ht / c); }
+public:
constexpr inline QSize toSize() const noexcept;
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
diff --git a/src/corelib/tools/qspan.h b/src/corelib/tools/qspan.h
new file mode 100644
index 0000000000..2671ba374f
--- /dev/null
+++ b/src/corelib/tools/qspan.h
@@ -0,0 +1,475 @@
+// Copyright (C) 2023 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
+
+#ifndef QSPAN_H
+#define QSPAN_H
+
+#include <QtCore/qcompilerdetection.h>
+#include <QtCore/qtypes.h>
+#include <QtCore/qcontainerfwd.h>
+
+#include <array>
+#include <cstddef>
+#include <cassert>
+#include <initializer_list>
+#include <QtCore/q20iterator.h>
+#include <QtCore/q20memory.h>
+#ifdef __cpp_lib_span
+#include <span>
+#endif
+#include <QtCore/q20type_traits.h>
+
+QT_BEGIN_NAMESPACE
+
+// like std::dynamic_extent
+namespace q20 {
+ inline constexpr auto dynamic_extent = std::size_t(-1);
+} // namespace q20
+
+QT_BEGIN_INCLUDE_NAMESPACE
+#ifdef __cpp_lib_span
+#ifdef __cpp_lib_concepts
+namespace std::ranges {
+// Officially, these are defined in <ranges>, but that is a heavy-hitter header.
+// OTOH, <span> must specialize these variable templates, too, so we assume that
+// <span> includes some meaningful subset of <ranges> and just go ahead and use them:
+template <typename T, std::size_t E>
+constexpr inline bool enable_borrowed_range<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
+template <typename T, std::size_t E>
+constexpr inline bool enable_view<QT_PREPEND_NAMESPACE(QSpan)<T, E>> = true;
+} // namespace std::ranges
+#endif // __cpp_lib_concepts
+#endif // __cpp_lib_span
+QT_END_INCLUDE_NAMESPACE
+
+namespace QSpanPrivate {
+
+template <typename T, std::size_t E> class QSpanBase;
+
+template <typename T>
+struct is_qspan_helper : std::false_type {};
+template <typename T, std::size_t E>
+struct is_qspan_helper<QSpan<T, E>> : std::true_type {};
+template <typename T, std::size_t E>
+struct is_qspan_helper<QSpanBase<T, E>> : std::true_type {};
+template <typename T>
+using is_qspan = is_qspan_helper<q20::remove_cvref_t<T>>;
+
+template <typename T>
+struct is_std_span_helper : std::false_type {};
+#ifdef __cpp_lib_span
+template <typename T, std::size_t E>
+struct is_std_span_helper<std::span<T, E>> : std::true_type {};
+#endif // __cpp_lib_span
+template <typename T>
+using is_std_span = is_std_span_helper<q20::remove_cvref_t<T>>;
+
+template <typename T>
+struct is_std_array_helper : std::false_type {};
+template <typename T, std::size_t N>
+struct is_std_array_helper<std::array<T, N>> : std::true_type {};
+template <typename T>
+using is_std_array = is_std_array_helper<q20::remove_cvref_t<T>>;
+
+template <typename From, typename To>
+using is_qualification_conversion =
+ std::is_convertible<From(*)[], To(*)[]>; // https://eel.is/c++draft/span.cons#note-1
+template <typename From, typename To>
+constexpr inline bool is_qualification_conversion_v = is_qualification_conversion<From, To>::value;
+
+namespace AdlTester {
+#define MAKE_ADL_TEST(what) \
+ using std:: what; /* bring into scope */ \
+ template <typename T> using what ## _result = decltype( what (std::declval<T&&>())); \
+ /* end */
+MAKE_ADL_TEST(begin)
+MAKE_ADL_TEST(data)
+MAKE_ADL_TEST(size)
+#undef MAKE_ADL_TEST
+}
+
+// Replacements for std::ranges::XXX(), but only bringing in ADL XXX()s,
+// not doing the extra work C++20 requires
+template <typename Range>
+AdlTester::begin_result<Range> adl_begin(Range &&r) { using std::begin; return begin(r); }
+template <typename Range>
+AdlTester::data_result<Range> adl_data(Range &&r) { using std::data; return data(r); }
+template <typename Range>
+AdlTester::size_result<Range> adl_size(Range &&r) { using std::size; return size(r); }
+
+// Replacement for std::ranges::iterator_t (which depends on C++20 std::ranges::begin)
+// This one uses adl_begin() instead.
+template <typename Range>
+using iterator_t = decltype(QSpanPrivate::adl_begin(std::declval<Range&>()));
+template <typename Range>
+using range_reference_t = q20::iter_reference_t<QSpanPrivate::iterator_t<Range>>;
+
+template <typename T>
+class QSpanCommon {
+protected:
+ template <typename Iterator>
+ using is_compatible_iterator = std::conjunction<
+ // ### C++20: extend to contiguous_iteratorss
+ std::is_base_of<
+ std::random_access_iterator_tag,
+ typename std::iterator_traits<Iterator>::iterator_category
+ >,
+ is_qualification_conversion<
+ std::remove_reference_t<q20::iter_reference_t<Iterator>>,
+ T
+ >
+ >;
+ template <typename Iterator, typename End>
+ using is_compatible_iterator_and_sentinel = std::conjunction<
+ // ### C++20: extend to contiguous_iterators and real sentinels
+ is_compatible_iterator<Iterator>,
+ std::negation<std::is_convertible<End, std::size_t>>
+ >;
+ template <typename Range, typename = void> // wrap use of SFINAE-unfriendly iterator_t:
+ struct is_compatible_range_helper : std::false_type {};
+ template <typename Range>
+ struct is_compatible_range_helper<Range, std::void_t<QSpanPrivate::iterator_t<Range>>>
+ : is_compatible_iterator<QSpanPrivate::iterator_t<Range>> {};
+ template <typename Range>
+ using is_compatible_range = std::conjunction<
+ // ### C++20: extend to contiguous_iterators
+ std::negation<is_qspan<Range>>,
+ std::negation<is_std_span<Range>>,
+ std::negation<is_std_array<Range>>,
+ std::negation<std::is_array<q20::remove_cvref_t<Range>>>,
+ is_compatible_range_helper<Range>
+ >;
+
+ // constraints
+ template <typename Iterator>
+ using if_compatible_iterator = std::enable_if_t<
+ is_compatible_iterator<Iterator>::value
+ , bool>;
+ template <typename Iterator, typename End>
+ using if_compatible_iterator_and_sentinel = std::enable_if_t<
+ is_compatible_iterator_and_sentinel<Iterator, End>::value
+ , bool>;
+ template <typename Range>
+ using if_compatible_range = std::enable_if_t<is_compatible_range<Range>::value, bool>;
+}; // class QSpanCommon
+
+template <typename T, std::size_t E>
+class QSpanBase : protected QSpanCommon<T>
+{
+ static_assert(E < size_t{(std::numeric_limits<qsizetype>::max)()},
+ "QSpan only supports extents that fit into the signed size type (qsizetype).");
+
+ struct Enabled_t { explicit Enabled_t() = default; };
+ static inline constexpr Enabled_t Enable{};
+
+ template <typename S, std::size_t N>
+ using if_compatible_array = std::enable_if_t<
+ N == E && is_qualification_conversion_v<S, T>
+ , bool>;
+
+ template <typename S>
+ using if_qualification_conversion = std::enable_if_t<
+ is_qualification_conversion_v<S, T>
+ , bool>;
+protected:
+ using Base = QSpanCommon<T>;
+
+ // data members:
+ T *m_data;
+ static constexpr qsizetype m_size = qsizetype(E);
+
+ // types and constants:
+ // (in QSpan only)
+
+ // constructors (need to be public d/t the way ctor inheriting works):
+public:
+ template <std::size_t E2 = E, std::enable_if_t<E2 == 0, bool> = true>
+ Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr} {}
+
+ template <typename It, typename Base::template if_compatible_iterator<It> = true>
+ explicit constexpr QSpanBase(It first, qsizetype count)
+ : m_data{q20::to_address(first)}
+ {
+ Q_ASSERT(count == m_size);
+ }
+
+ template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
+ explicit constexpr QSpanBase(It first, End last)
+ : QSpanBase(first, last - first) {}
+
+ template <size_t N, std::enable_if_t<N == E, bool> = true>
+ Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
+ : QSpanBase(arr, N) {}
+
+ template <typename S, size_t N, if_compatible_array<S, N> = true>
+ Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
+ : QSpanBase(arr.data(), N) {}
+
+ template <typename S, size_t N, if_compatible_array<S, N> = true>
+ Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
+ : QSpanBase(arr.data(), N) {}
+
+ template <typename Range, typename Base::template if_compatible_range<Range> = true>
+ Q_IMPLICIT constexpr QSpanBase(Range &&r)
+ : QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
+ qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
+ {}
+
+ template <typename S, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(QSpan<S, E> other) noexcept
+ : QSpanBase(other.data(), other.size())
+ {}
+
+ template <typename S, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(QSpan<S> other)
+ : QSpanBase(other.data(), other.size())
+ {}
+
+ template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
+ Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il)
+ : QSpanBase(il.begin(), il.size())
+ {}
+
+#ifdef __cpp_lib_span
+ template <typename S, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(std::span<S, E> other) noexcept
+ : QSpanBase(other.data(), other.size())
+ {}
+
+ template <typename S, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(std::span<S> other)
+ : QSpanBase(other.data(), other.size())
+ {}
+#endif // __cpp_lib_span
+}; // class QSpanBase (fixed extent)
+
+template <typename T>
+class QSpanBase<T, q20::dynamic_extent> : protected QSpanCommon<T>
+{
+ template <typename S>
+ using if_qualification_conversion = std::enable_if_t<
+ is_qualification_conversion_v<S, T>
+ , bool>;
+protected:
+ using Base = QSpanCommon<T>;
+
+ // data members:
+ T *m_data;
+ qsizetype m_size;
+
+ // constructors (need to be public d/t the way ctor inheriting works):
+public:
+ Q_IMPLICIT constexpr QSpanBase() noexcept : m_data{nullptr}, m_size{0} {}
+
+ template <typename It, typename Base::template if_compatible_iterator<It> = true>
+ Q_IMPLICIT constexpr QSpanBase(It first, qsizetype count)
+ : m_data{q20::to_address(first)}, m_size{count} {}
+
+ template <typename It, typename End, typename Base::template if_compatible_iterator_and_sentinel<It, End> = true>
+ Q_IMPLICIT constexpr QSpanBase(It first, End last)
+ : QSpanBase(first, last - first) {}
+
+ template <size_t N>
+ Q_IMPLICIT constexpr QSpanBase(q20::type_identity_t<T> (&arr)[N]) noexcept
+ : QSpanBase(arr, N) {}
+
+ template <typename S, size_t N, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(std::array<S, N> &arr) noexcept
+ : QSpanBase(arr.data(), N) {}
+
+ template <typename S, size_t N, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(const std::array<S, N> &arr) noexcept
+ : QSpanBase(arr.data(), N) {}
+
+ template <typename Range, typename Base::template if_compatible_range<Range> = true>
+ Q_IMPLICIT constexpr QSpanBase(Range &&r)
+ : QSpanBase(QSpanPrivate::adl_data(r), // no forward<>() here (std doesn't have it, either)
+ qsizetype(QSpanPrivate::adl_size(r))) // ditto, no forward<>()
+ {}
+
+ template <typename S, size_t N, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(QSpan<S, N> other) noexcept
+ : QSpanBase(other.data(), other.size())
+ {}
+
+ template <typename U = T, std::enable_if_t<std::is_const_v<U>, bool> = true>
+ Q_IMPLICIT constexpr QSpanBase(std::initializer_list<std::remove_cv_t<T>> il) noexcept
+ : QSpanBase(il.begin(), il.size())
+ {}
+
+#ifdef __cpp_lib_span
+ template <typename S, size_t N, if_qualification_conversion<S> = true>
+ Q_IMPLICIT constexpr QSpanBase(std::span<S, N> other) noexcept
+ : QSpanBase(other.data(), other.size())
+ {}
+#endif // __cpp_lib_span
+}; // class QSpanBase (dynamic extent)
+
+} // namespace QSpanPrivate
+
+template <typename T, std::size_t E>
+class QSpan
+#ifndef Q_QDOC
+ : private QSpanPrivate::QSpanBase<T, E>
+#endif
+{
+ using Base = QSpanPrivate::QSpanBase<T, E>;
+ Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
+ [[maybe_unused]] qsizetype n = 1) const
+ {
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(pos <= size());
+ Q_ASSERT(n >= 0);
+ Q_ASSERT(n <= size() - pos);
+ }
+
+ template <std::size_t N>
+ static constexpr bool subspan_always_succeeds_v = N <= E && E != q20::dynamic_extent;
+public:
+ // constants and types
+ using value_type = std::remove_cv_t<T>;
+#ifdef QT_COMPILER_HAS_LWG3346
+ using iterator_concept = std::contiguous_iterator_tag;
+ using element_type = T;
+#endif
+ using size_type = qsizetype; // difference to std::span
+ using difference_type = qptrdiff; // difference to std::span
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+ using iterator = pointer; // implementation-defined choice
+ using const_iterator = const_pointer; // implementation-defined choice
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ static constexpr std::size_t extent = E;
+
+ // [span.cons], constructors, copy, and assignment
+ using Base::Base;
+#ifdef Q_QDOC
+ template <typename It> using if_compatible_iterator = bool;
+ template <typename S> using if_qualification_conversion = bool;
+ template <typename Range> using if_compatible_range = bool;
+ template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, qsizetype count);
+ template <typename It, if_compatible_iterator<It> = true> constexpr QSpan(It first, It last);
+ template <size_t N> constexpr QSpan(q20::type_identity_t<T> (&arr)[N]) noexcept;
+ template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::array<S, N> &arr) noexcept;
+ template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(const std::array<S, N> &arr) noexcept;
+ template <typename Range, if_compatible_range<Range> = true> constexpr QSpan(Range &&r);
+ template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(QSpan<S, N> other) noexcept;
+ template <typename S, size_t N, if_qualification_conversion<S> = true> constexpr QSpan(std::span<S, N> other) noexcept;
+ constexpr QSpan(std::initializer_list<value_type> il);
+#endif // Q_QDOC
+
+ // [span.obs]
+ [[nodiscard]] constexpr size_type size() const noexcept { return this->m_size; }
+ [[nodiscard]] constexpr size_type size_bytes() const noexcept { return size() * sizeof(T); }
+ [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; }
+
+ // [span.elem]
+ [[nodiscard]] constexpr reference operator[](size_type idx) const
+ { verify(idx); return data()[idx]; }
+ [[nodiscard]] constexpr reference front() const { verify(); return *data(); }
+ [[nodiscard]] constexpr reference back() const { verify(); return data()[size() - 1]; }
+ [[nodiscard]] constexpr pointer data() const noexcept { return this->m_data; }
+
+ // [span.iterators]
+ [[nodiscard]] constexpr iterator begin() const noexcept { return data(); }
+ [[nodiscard]] constexpr iterator end() const noexcept { return data() + size(); }
+ [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); }
+ [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); }
+ [[nodiscard]] constexpr reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; }
+ [[nodiscard]] constexpr reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; }
+ [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+ [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); }
+
+ // [span.sub]
+ template <std::size_t Count>
+ [[nodiscard]] constexpr QSpan<T, Count> first() const
+ noexcept(subspan_always_succeeds_v<Count>)
+ {
+ static_assert(Count <= E,
+ "Count cannot be larger than the span's extent.");
+ verify(0, Count);
+ return QSpan<T, Count>{data(), Count};
+ }
+
+ template <std::size_t Count>
+ [[nodiscard]] constexpr QSpan<T, Count> last() const
+ noexcept(subspan_always_succeeds_v<Count>)
+ {
+ static_assert(Count <= E,
+ "Count cannot be larger than the span's extent.");
+ verify(0, Count);
+ return QSpan<T, Count>{data() + (size() - Count), Count};
+ }
+
+ template <std::size_t Offset>
+ [[nodiscard]] constexpr auto subspan() const
+ noexcept(subspan_always_succeeds_v<Offset>)
+ {
+ static_assert(Offset <= E,
+ "Offset cannot be larger than the span's extent.");
+ verify(Offset, 0);
+ if constexpr (E == q20::dynamic_extent)
+ return QSpan<T>{data() + Offset, qsizetype(size() - Offset)};
+ else
+ return QSpan<T, E - Offset>{data() + Offset, qsizetype(E - Offset)};
+ }
+
+ template <std::size_t Offset, std::size_t Count>
+ [[nodiscard]] constexpr auto subspan() const
+ noexcept(subspan_always_succeeds_v<Offset + Count>)
+ { return subspan<Offset>().template first<Count>(); }
+
+ [[nodiscard]] constexpr QSpan<T> first(size_type n) const { verify(0, n); return {data(), n}; }
+ [[nodiscard]] constexpr QSpan<T> last(size_type n) const { verify(0, n); return {data() + (size() - n), n}; }
+ [[nodiscard]] constexpr QSpan<T> subspan(size_type pos) const { verify(pos, 0); return {data() + pos, size() - pos}; }
+ [[nodiscard]] constexpr QSpan<T> subspan(size_type pos, size_type n) const { return subspan(pos).first(n); }
+
+ // Qt-compatibility API:
+ [[nodiscard]] bool isEmpty() const noexcept { return empty(); }
+ // nullary first()/last() clash with first<>() and last<>(), so they're not provided for QSpan
+ [[nodiscard]] constexpr QSpan<T> sliced(size_type pos) const { return subspan(pos); }
+ [[nodiscard]] constexpr QSpan<T> sliced(size_type pos, size_type n) const { return subspan(pos, n); }
+
+private:
+ // [span.objectrep]
+ [[nodiscard]] friend
+ QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>
+ as_bytes(QSpan s) noexcept
+ {
+ using R = QSpan<const std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>;
+ return R{reinterpret_cast<const std::byte *>(s.data()), s.size_bytes()};
+ }
+
+ template <typename U>
+ using if_mutable = std::enable_if_t<!std::is_const_v<U>, bool>;
+
+#ifndef Q_QDOC
+ template <typename T2 = T, if_mutable<T2> = true>
+#endif
+ [[nodiscard]] friend
+ QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>
+ as_writable_bytes(QSpan s) noexcept
+ {
+ using R = QSpan<std::byte, E == q20::dynamic_extent ? q20::dynamic_extent : E * sizeof(T)>;
+ return R{reinterpret_cast<std::byte *>(s.data()), s.size_bytes()};
+ }
+}; // class QSpan
+
+// [span.deduct]
+template <class It, class EndOrSize>
+QSpan(It, EndOrSize) -> QSpan<std::remove_reference_t<q20::iter_reference_t<It>>>;
+template <class T, std::size_t N>
+QSpan(T (&)[N]) -> QSpan<T, N>;
+template <class T, std::size_t N>
+QSpan(std::array<T, N> &) -> QSpan<T, N>;
+template <class T, std::size_t N>
+QSpan(const std::array<T, N> &) -> QSpan<const T, N>;
+template <class R>
+QSpan(R&&) -> QSpan<std::remove_reference_t<QSpanPrivate::range_reference_t<R>>>;
+
+QT_END_NAMESPACE
+
+#endif // QSPAN_H
diff --git a/src/corelib/tools/qspan.qdoc b/src/corelib/tools/qspan.qdoc
new file mode 100644
index 0000000000..9b55b09bf7
--- /dev/null
+++ b/src/corelib/tools/qspan.qdoc
@@ -0,0 +1,683 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \class QSpan
+ \inmodule QtCore
+ \since 6.7
+ \brief A non-owning container over contiguous data.
+ \ingroup tools
+ \reentrant
+
+ A QSpan references a contiguous portion of another contiguous container.
+ It acts as an interface type for all kinds of contiguous containers,
+ without the need to construct an owning container such as QList or
+ std::vector first.
+
+ The data referenced by a QSpan may be represented as an array (or
+ array-compatible data-structure such as QList, std::vector,
+ QVarLengthArray, etc.). QSpan itself merely stores a pointer to the data,
+ so users must ensure that QSpan objects do not outlive the data they
+ reference.
+
+ Unlike views such as QStringView, QLatin1StringView and QUtf8StringView,
+ referenced data can be modified through a QSpan object. To prevent this,
+ construct a QSpan over a \c{const T}:
+
+ \code
+ int numbers[] = {0, 1, 2};
+ QSpan<int> span = numbers;
+ span[0] = 42;
+ // numbers == {42, 1, 2};
+ QSpan<const int> cspan = numbers;
+ cspan[0] = 0; // ERROR: cspan[0] is read-only
+ \endcode
+
+ A QSpan can be \e{fixed-size} or \e{variable-sized}.
+
+ A variable-sized span is formed by omitting the second template argument
+ (or setting it to \c{std::dynamic_extent}, which is, however, only
+ available in C++20 builds), as seen in the example above.
+
+ A fixed-size span is formed by passing a number as the second template
+ argument:
+
+ \code
+ int numbers[] = {0, 1, 2};
+ QSpan<int, 3> span = numbers;
+ QSpan<const int, 3> = numbers; // also OK
+ \endcode
+
+ As the name suggests, a fixed-size span's size() is fixed at compile-time
+ whereas the size() of a variable-sized span is determined only at run-time.
+
+ A fixed-size span is not default-constructible (unless its \l extent is zero
+ (0)). A variable-sized span \e{is} default-constructible and will have
+ \c{data() == nullptr} and \c{size() == 0}.
+
+ A fixed-size span can be implicitly converted into a variable-sized one.
+ The opposite direction (variable-length into fixed-length) has the
+ precondition that both span's sizes must match.
+
+ Unlike with owning containers, \c{const} is \e{shallow} in QSpan: you can
+ still modify the data through a const QSpan (but not through a
+ \c{QSpan<const T>}), and begin() and end() are not overloaded on
+ \c{const}/non-\c{const}. There are cbegin() and cend(), though, that return
+ const_iterators which prevent modification of the data even though \c{T} is
+ not const:
+ \code
+ int numbers[] = {0, 1, 2};
+ const QSpan<int> span = numbers;
+ span.front() = 42; // OK, numbers[0] == 42 now
+ *span.begin() = 31; // OK, numbers[0] == 31 now
+ *span.cbegin() = -1; // ERROR: cannot assign through a const_iterator
+ \endcode
+
+ QSpan should be passed by value, not by reference-to-const:
+
+ \code
+ void consume(QSpan<const int> data); // OK
+ void consume(const QSpan<const int> &data); // works, but is non-idiomatic and less efficient
+ \endcode
+
+ \c{QSpan<T,N>} is a \e{Literal Type}, regardless of whether \c{T} is a
+ Literal Type or not.
+
+ \section2 QSpan vs. std::span
+ \target span-STL
+
+ QSpan is closely modelled after
+ \l{https://en.cppreference.com/w/cpp/container/span}{std::span}, but has a
+ few differences which we'll discuss here. Since they both implicitly
+ convert into each other, you're free to choose whichever one you like best
+ in your own code.
+
+ \list
+ \li QSpan is using the signed qsizetype as \c{size_type}
+ whereas \c{std::span} uses \c{size_t}.
+ \li All QSpan constructors are implicit;
+ many \c{std::span} ones are \c{explicit}.
+ \li QSpan can be constructed from rvalue owning containers, \c{std::span} can not.
+ \endlist
+
+ The last two are required for source-compatibility when functions that took
+ owning containers are converted to take QSpan instead, which is a
+ vitally-important use-case in Qt. The use of qsizetype is for consistency
+ with the rest of Qt containers. QSpan template arguments still use size_t
+ to avoid introducing unnecessary error conditions (negative sizes).
+
+ \section2 Compatible Iterators
+ \target span-compatible-iterators
+
+ QSpan can be constructed from an iterator and size or from an
+ iterator pair, provided the iterators are \e{compatible} ones.
+ Eventually, this should mean C++20 \c{std::contiguous_iterator} and
+ \c{std::sentinel_for}, but while Qt still supports C++17, only raw pointers
+ are considered contiguous iterators.
+
+ \section2 Compatible Ranges
+ \target span-compatible-ranges
+
+ QSpan can also be constructed from a \e{compatible} range. A range is
+ compatible if it has \l{span-compatible-iterators}{compatible iterators}.
+
+ \sa QList, QStringView, QLatin1StringView, QUtf8StringView
+*/
+
+//
+// Nested types and constants
+//
+
+/*!
+ \typedef QSpan::element_type
+
+ An alias for \c{T}. Includes the \c{const}, if any.
+
+ This alias is provided for compatbility with the STL.
+
+ \sa value_type, pointer
+*/
+
+/*!
+ \typedef QSpan::value_type
+
+ An alias for \c{T}. Excludes the \c{const}, if any.
+
+ This alias is provided for compatbility with the STL.
+
+ \sa element_type
+*/
+
+/*!
+ \typedef QSpan::size_type
+
+ An alias for qsizetype. This \l{span-STL}{differs from \c{std::span}}.
+
+ This alias is provided for compatbility with the STL.
+*/
+
+/*!
+ \typedef QSpan::difference_type
+
+ An alias for qptrdiff. This \l{span-STL}{differs from \c{std::span}}.
+
+ This alias is provided for compatbility with the STL.
+*/
+
+/*!
+ \typedef QSpan::pointer
+
+ An alias for \c{T*} and \c{element_type*}, respectively. Includes the \c{const}, if any.
+
+ This alias is provided for compatbility with the STL.
+
+ \sa element_type, const_pointer, reference, iterator
+*/
+
+/*!
+ \typedef QSpan::const_pointer
+
+ An alias for \c{const T*} and \c{const element_type*}, respectively.
+
+ This alias is provided for compatbility with the STL.
+
+ \sa element_type, pointer, const_reference, const_iterator
+*/
+
+/*!
+ \typedef QSpan::reference
+
+ An alias for \c{T&} and \c{element_type&}, respectively. Includes the \c{const}, if any.
+
+ This alias is provided for compatbility with the STL.
+
+ \sa element_type, const_reference, pointer
+*/
+
+/*!
+ \typedef QSpan::const_reference
+
+ An alias for \c{const T&} and \c{const element_type&}, respectively.
+
+ This alias is provided for compatbility with the STL.
+
+ \sa element_type, reference, const_pointer
+*/
+
+/*!
+ \typedef QSpan::iterator
+
+ An alias for \c{T*} and \c{pointer}, respectively. Includes the \c{const}, if any.
+
+ \sa pointer, const_iterator, reverse_iterator
+*/
+
+/*!
+ \typedef QSpan::const_iterator
+
+ An alias for \c{const T*} and \c{const_pointer}, respectively.
+
+ \sa const_pointer, iterator, const_reverse_iterator
+*/
+
+/*!
+ \typedef QSpan::reverse_iterator
+
+ An alias for \c{std::reverse_iterator<iterator>}. Includes the \c{const}, if any.
+
+ \sa iterator, const_reverse_iterator
+*/
+
+/*!
+ \typedef QSpan::const_reverse_iterator
+
+ An alias for \c{std::reverse_iterator<const_iterator>}.
+
+ \sa const_iterator, reverse_iterator
+*/
+
+/*!
+ \variable QSpan::extent
+
+ The second template argument of \c{QSpan<T, E>}, that is, \c{E}. This is
+ \c{std::dynamic_extent} for variable-sized spans.
+
+ \note While all other sizes and indexes in QSpan use qsizetype, this
+ variable, like \c{E}, is actually of type \c{size_t}, for compatibility with
+ \c{std::span} and \c{std::dynamic_extent}.
+
+ \sa size()
+*/
+
+//
+// Constructors and SMFs
+//
+
+/*!
+ \fn template <typename T, size_t E> QSpan<T,E>::QSpan()
+
+ Default constructor.
+
+ This constructor is only present if \c{E} is either zero (0) or
+ \c{std::dynamic_extent}. In other words: only fixed-zero-sized or variable-sized spans
+ are default-constructible.
+
+ \sa extent
+*/
+
+/*!
+ \fn template <typename T, size_t E> QSpan<T,E>::QSpan(const QSpan &other)
+ \fn template <typename T, size_t E> QSpan<T,E>::QSpan(QSpan &&other)
+ \fn template <typename T, size_t E> QSpan<T,E> &QSpan<T,E>::operator=(const QSpan &other)
+ \fn template <typename T, size_t E> QSpan<T,E> &QSpan<T,E>::operator=(QSpan &&other)
+ \fn template <typename T, size_t E> QSpan<T,E>::~QSpan()
+
+ These Special Member Functions are implicitly-defined.
+
+ \note Moves are equivalent to copies. Only data() and size() are copied
+ from span to span, not the referenced data.
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <typename It, QSpan<T, E>::if_compatible_iterator<It>> QSpan<T,E>::QSpan(It first, qsizetype count)
+
+ Constructs a QSpan referencing the data starting at \a first and having length
+ \a count.
+
+ \c{[first, count)} must be a valid range.
+
+ \note This constructor participates in overload resolution only if \c{It}
+ is \l{span-compatible-iterators}{a compatible iterator}.
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <typename It, QSpan<T, E>::if_compatible_iterator<It>> QSpan<T,E>::QSpan(It first, It last)
+
+ Constructs a QSpan referencing the data starting at \a first and having length
+ (\a last - \a first).
+
+ \c{[first, last)} must be a valid range.
+
+ \note This constructor participates in overload resolution only if \c{It}
+ is \l{span-compatible-iterators}{a compatible iterator}.
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <size_t N> QSpan<T,E>::QSpan(q20::type_identity_t<T> (&arr)[N]);
+ \fn template <typename T, size_t E> template <typename S, size_t N, QSpan<T, E>::if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(std::array<S, N> &arr);
+ \fn template <typename T, size_t E> template <typename S, size_t N, QSpan<T, E>::if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(const std::array<S, N> &arr);
+
+ Constructs a QSpan referencing the data in the supplied array \a arr.
+
+ \note This constructor participates in overload resolution only if
+ \list
+ \li either \c{N} or \l{extent} are \c{std::dynamic_extent} or otherwise \l{extent} \c{==} \c{N}
+ \li and either \c{S} or \c{const S} are the same as \c{T}.
+ \endlist
+
+ \note \c{q20::type_identity_t} is a C++17 backport of C++20's
+ \l{https://en.cppreference.com/w/cpp/types/type_identity}{\c{std::type_identity_t}}.
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <typename Range, QSpan<T, E>::if_compatible_range<Range> = true> QSpan<T,E>::QSpan(Range &&r)
+
+ Constructs a QSpan referencing the data in the supplied range \a r.
+
+ \note This constructor participates in overload resolution only if \c{Range}
+ is \l{span-compatible-ranges}{a compatible range}.
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <typename S, size_t N, QSpan<T, E>::if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(QSpan<S, N> other);
+ \fn template <typename T, size_t E> template <typename S, size_t N, QSpan<T, E>::if_qualification_conversion<S> = true> QSpan<T,E>::QSpan(std::span<S, N> other);
+
+ Constructs a QSpan referencing the data in the supplied span \a other.
+
+ \note This constructor participates in overload resolution only if
+ \list
+ \li either \c{N} or \l{extent} are \c{std::dynamic_extent} or \l{extent} \c{==} \c{N}
+ \li and either \c{S} or \c{const S} are the same as \c{T}.
+ \endlist
+*/
+
+/*!
+ \fn template <typename T, size_t E> QSpan<T, E>::QSpan(std::initializer_list<value_type> il);
+
+ Constructs a QSpan referencing the data in the supplied initializer list \a il.
+
+ \note This constructor participates in overload resolution only if \c{T} is \c{const}-qualified.
+
+ \note This constructor is \c{noexcept} only if \c{E} is \c{std::dynamic_extent}.
+
+ \note If \c{E} is not \c{std::dynamic_extent} and the size of \a il is not \c{E}, the behavior is undefined.
+*/
+
+//
+// Member functions: sizes
+//
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::size() const
+
+ Returns the size of the span, that is, the number of elements it references.
+
+ \sa size_bytes(), empty(), isEmpty()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::size_bytes() const
+
+ Returns the size of the span in bytes, that is, the number of elements
+ multiplied by \c{sizeof(T)}.
+
+ \sa size(), empty(), isEmpty()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::empty() const
+ \fn template <typename T, size_t E> auto QSpan<T, E>::isEmpty() const
+
+ Returns whether the span is empty, that is, whether \c{size() == 0}.
+
+ These functions do the same thing: empty() is provided for STL
+ compatibility and isEmpty() is provided for Qt compatibility.
+
+ \sa size(), size_bytes()
+*/
+
+//
+// element access
+//
+
+/*!
+ \fn template <typename T, size_t E> QSpan<T, E>::operator[](size_type idx) const
+
+ Returns a reference to the element at index \a idx in the span.
+
+ The index must be in range, that is, \a idx >= 0 and \a idx < size(),
+ otherwise the behavior is undefined.
+
+ \sa front(), back(), size(), empty()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::front() const
+
+ Returns a reference to the first element in the span.
+
+ The span must not be empty, otherwise the behavior is undefined.
+
+ \sa operator[](), back(), size(), empty()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::back() const
+
+ Returns a reference to the last element in the span.
+
+ The span must not be empty, otherwise the behavior is undefined.
+
+ \sa operator[](), front(), size(), empty()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::data() const
+
+ Returns a pointer to the beginning of the span.
+
+ The same as calling begin().
+
+ \sa begin(), front()
+*/
+
+//
+// iterators
+//
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::begin() const
+
+ Returns an interator pointing at the beginning of the span.
+
+ Because QSpan iterators are just pointers, this is the same as calling
+ data().
+
+ \sa end(), cbegin(), rbegin(), crbegin(), data()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::end() const
+
+ Returns an iterator pointing to one past the end of the span.
+
+ Because QSpan iterators are just pointers, this it the same as calling
+ \c{data() + size()}.
+
+ \sa begin(), cend(), rend(), crend(), data(), size()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::cbegin() const
+
+ Returns a const_iterator pointing to the beginning of the span.
+
+ This will return a read-only iterator even if \c{T} is not \c{const}:
+ \code
+ QSpan<int> span = ~~~;
+ *span.begin() = 42; // OK
+ *span.cbegin() = 42; // ERROR: cannot assign through a const_iterator
+ \endcode
+
+ \sa cend(), begin(), crbegin(), rbegin(), data()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::cend() const
+
+ Returns a const_iterator pointing to one past the end of the span.
+
+ \sa cbegin(), end(), crend(), rend(), data(), size()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::rbegin() const
+
+ Returns a reverse_iterator pointing to the beginning of the reversed span.
+
+ \sa rend(), crbegin(), begin(), cbegin()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::rend() const
+
+ Returns a reverse_iterator pointing to one past the end of the reversed span.
+
+ \sa rbegin(), crend(), end(), cend()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::crbegin() const
+
+ Returns a const_reverse_iterator pointing to the beginning of the reversed span.
+
+ \sa crend(), rbegin(), cbegin(), begin()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::crend() const
+
+ Returns a const_reverse_iterator pointing to one past the end of the reversed span.
+
+ \sa crbegin(), rend(), cend(), end()
+*/
+
+//
+// compile-time subspans:
+//
+
+/*!
+ \fn template <typename T, size_t E> template <std::size_t Count> auto QSpan<T, E>::first() const
+ \keyword first-t
+
+ Returns a fixed-sized span of size \c{Count} referencing the first \c{Count} elements of \c{*this}.
+
+ The span must hold at least \c{Count} elements (\c{E} >= \c{Count} \e{and}
+ size() >= \c{Count}), otherwise the behavior is undefined.
+
+ \sa first(QSpan<T,E>::size_type), last(), subspan()
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <std::size_t Count> auto QSpan<T, E>::last() const
+ \keyword last-t
+
+ Returns a fixed-sized span of size \c{Count} referencing the last \c{Count} elements of \c{*this}.
+
+ The span must hold at least \c{Count} elements (\c{E} >= \c{Count} \e{and}
+ size() >= \c{Count}), otherwise the behavior is undefined.
+
+ \sa last(QSpan<T,E>::size_type), first(), subspan()
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <std::size_t Offset> auto QSpan<T, E>::subspan() const
+ \keyword subspan-t1
+
+ Returns a span of size \c{E - Offset} referencing the remainder of this span
+ after dropping the first \c{Offset} elements.
+
+ If \c{*this} is a variable-sized span, the return type is a variable-sized
+ span, otherwise it is a fixed-sized span.
+
+ This span must hold at least \c{Offset} elements (\c{E} >= \c{Offset} \e{and}
+ size() >= \c{Offset}), otherwise the behavior is undefined.
+
+ \sa subspan(QSpan<T,E>::size_type), subspan(), first(), last()
+*/
+
+/*!
+ \fn template <typename T, size_t E> template <std::size_t Offset, std::size_t Count> auto QSpan<T, E>::subspan() const
+ \keyword subspan-t2
+
+ Returns a span of size \c{Count} referencing the \c{Count} elements of this
+ span starting at \c{Offset}.
+
+ If \c{*this} is a variable-sized span, the return type is a variable-sized
+ span, otherwise it is a fixed-sized span.
+
+ This span must hold at least \c{Offset + Count} elements (\c{E} >=
+ \c{Offset + Count} \e{and} size() >= \c{Offset + Count}), otherwise the
+ behavior is undefined.
+
+ \sa subspan(QSpan<T,E>::size_type, QSpan<T,E>::size_type), subspan(), first(), last()
+*/
+
+//
+// runtime subspans:
+//
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::first(qsizetype n) const
+ \keyword first-n
+
+ Returns a variable-sized span of size \a n referencing the first \a n elements of \c{*this}.
+
+ \a n must be non-negative.
+
+ The span must hold at least \a n elements (\c{E} >= \a n \e{and} size() >=
+ \a n), otherwise the behavior is undefined.
+
+ \sa {first-t}{first<N>()}, last(QSpan<T,E>::size_type), subspan(QSpan<T,E>::size_type),
+ subspan(QSpan<T,E>::size_type, QSpan<T,E>::size_type)
+ \sa sliced()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::last(qsizetype n) const
+ \keyword last-n
+
+ Returns a variable-sized span of size \a n referencing the last \a n elements of \c{*this}.
+
+ \a n must be non-negative.
+
+ The span must hold at least \a n elements (\c{E} >= \a n \e{and}
+ size() >= \a n), otherwise the behavior is undefined.
+
+ \sa last(), first(QSpan<T,E>::size_type), subspan(QSpan<T,E>::size_type),
+ subspan(QSpan<T,E>::size_type, QSpan<T,E>::size_type), sliced()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::subspan(qsizetype pos) const
+ \fn template <typename T, size_t E> auto QSpan<T, E>::sliced(qsizetype pos) const
+ \keyword subspan-n1
+
+ Returns a variable-sized span of size \c{size() - pos} referencing the
+ remainder of this span after dropping the first \a pos elements.
+
+ \a pos must be non-negative.
+
+ This span must hold at least \a pos elements (\c{E} >= \a pos \e{and}
+ size() >= \a pos), otherwise the behavior is undefined.
+
+ These functions do the same thing: subspan() is provided for STL
+ compatibility and sliced() is provided for Qt compatibility.
+
+ \sa subspan(), first(QSpan<T,E>::size_type), last(QSpan<T,E>::size_type)
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::subspan(qsizetype pos, qsizetype n) const
+ \fn template <typename T, size_t E> auto QSpan<T, E>::sliced(qsizetype pos, qsizetype n) const
+ \keyword subspan-n2
+
+ Returns a variable-sized span of size \a n referencing the \a n elements of
+ this span starting at \a pos.
+
+ Both \a pos and \a n must be non-negative.
+
+ This span must hold at least \c{pos + n} elements (\c{E} >=
+ \c{pos + n} \e{and} size() >= \c{pos + n}), otherwise the
+ behavior is undefined.
+
+ These functions do the same thing: subspan() is provided for STL
+ compatibility and sliced() is provided for Qt compatibility.
+
+ \sa subspan(), first(QSpan<T,E>::size_type), last(QSpan<T,E>::size_type)
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::as_bytes(QSpan s)
+ \since 6.8
+
+ Returns \a s as a \c{QSpan<const std::byte, E'>} whose size() is equal to
+ \c{s.size_bytes()}.
+
+ If \c{E} is \c{std::dynamic_extent} then so is \c{E'}.
+ Otherwise, \c{E' = E * sizeof(T)}.
+
+ \note \c{q20::dynamic_extent} is a C++17 backport of C++20's
+ \l{https://en.cppreference.com/w/cpp/container/span/dynamic_extent}{\c{std::dynamic_extent}}.
+
+ \sa as_writable_bytes(), size_bytes()
+*/
+
+/*!
+ \fn template <typename T, size_t E> auto QSpan<T, E>::as_writable_bytes(QSpan s)
+ \since 6.8
+
+ Returns \a s as a \c{QSpan<std::byte, E'>} whose size() is equal to
+ \c{s.size_bytes()}.
+
+ If \c{E} is \c{std::dynamic_extent} then so is \c{E'}.
+ Otherwise, \c{E' = E * sizeof(T)}.
+
+ \note This function participates in overload resolution only if
+ \c{!std::is_const_v<T>}.
+
+ \note \c{q20::dynamic_extent} is a C++17 backport of C++20's
+ \l{https://en.cppreference.com/w/cpp/container/span/dynamic_extent}{\c{std::dynamic_extent}}.
+
+ \sa as_bytes(), size_bytes()
+*/
diff --git a/src/corelib/tools/qspan_p.h b/src/corelib/tools/qspan_p.h
new file mode 100644
index 0000000000..0072e3ef64
--- /dev/null
+++ b/src/corelib/tools/qspan_p.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 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
+
+#ifndef QSPAN_P_H
+#define QSPAN_P_H
+
+#include <QtCore/qspan.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
+
+#endif // QSPAN_P_H
diff --git a/src/corelib/tools/qtaggedpointer.h b/src/corelib/tools/qtaggedpointer.h
index bc43f765aa..6c467d59f8 100644
--- a/src/corelib/tools/qtaggedpointer.h
+++ b/src/corelib/tools/qtaggedpointer.h
@@ -43,10 +43,10 @@ public:
static constexpr quintptr tagMask() { return QtPrivate::TagInfo<T>::alignment - 1; }
static constexpr quintptr pointerMask() { return ~tagMask(); }
- constexpr QTaggedPointer() noexcept : d(0) {}
- constexpr QTaggedPointer(std::nullptr_t) noexcept : QTaggedPointer() {}
+ Q_NODISCARD_CTOR constexpr QTaggedPointer() noexcept : d(0) {}
+ Q_NODISCARD_CTOR constexpr QTaggedPointer(std::nullptr_t) noexcept : QTaggedPointer() {}
- explicit QTaggedPointer(T *pointer, Tag tag = Tag()) noexcept
+ Q_NODISCARD_CTOR explicit QTaggedPointer(T *pointer, Tag tag = Tag()) noexcept
: d(quintptr(pointer) | quintptr(tag))
{
static_assert(sizeof(Type*) == sizeof(QTaggedPointer));
diff --git a/src/corelib/tools/qtimeline.cpp b/src/corelib/tools/qtimeline.cpp
index 3bfe000bca..5512da867f 100644
--- a/src/corelib/tools/qtimeline.cpp
+++ b/src/corelib/tools/qtimeline.cpp
@@ -55,16 +55,16 @@ void QTimeLinePrivate::setCurrentTime(int msecs)
{
Q_Q(QTimeLine);
currentTime.removeBindingUnlessInWrapper();
- auto previousCurrentTime = currentTime.value();
+ const auto previousCurrentTime = currentTime.valueBypassingBindings();
- qreal lastValue = q->currentValue();
- int lastFrame = q->currentFrame();
+ const qreal lastValue = q->valueForTime(previousCurrentTime);
+ const int lastFrame = q->frameForTime(previousCurrentTime);
// Determine if we are looping.
- int elapsed = (direction == QTimeLine::Backward) ? (-msecs + duration) : msecs;
- int loopCountNow = elapsed / duration;
+ const int elapsed = (direction == QTimeLine::Backward) ? (-msecs + duration) : msecs;
+ const int loopCountNow = elapsed / duration;
- bool looping = (loopCountNow != currentLoopCount);
+ const bool looping = (loopCountNow != currentLoopCount);
#ifdef QTIMELINE_DEBUG
qDebug() << "QTimeLinePrivate::setCurrentTime:" << msecs << duration << "with loopCountNow"
<< loopCountNow << "currentLoopCount" << currentLoopCount << "looping" << looping;
@@ -75,7 +75,7 @@ void QTimeLinePrivate::setCurrentTime(int msecs)
// Normalize msecs to be between 0 and duration, inclusive.
currentTime.setValueBypassingBindings(elapsed % duration);
if (direction.value() == QTimeLine::Backward)
- currentTime.setValueBypassingBindings(duration - currentTime);
+ currentTime.setValueBypassingBindings(duration - currentTime.valueBypassingBindings());
// Check if we have reached the end of loopcount.
bool finished = false;
@@ -85,12 +85,14 @@ void QTimeLinePrivate::setCurrentTime(int msecs)
currentLoopCount = loopCount - 1;
}
- int currentFrame = q->frameForTime(currentTime);
+ const int currentFrame = q->frameForTime(currentTime.valueBypassingBindings());
#ifdef QTIMELINE_DEBUG
- qDebug() << "QTimeLinePrivate::setCurrentTime: frameForTime" << currentTime << currentFrame;
+ qDebug() << "QTimeLinePrivate::setCurrentTime: frameForTime"
+ << currentTime.valueBypassingBindings() << currentFrame;
#endif
- if (!qFuzzyCompare(lastValue, q->currentValue()))
- emit q->valueChanged(q->currentValue(), QTimeLine::QPrivateSignal());
+ const qreal currentValue = q->valueForTime(currentTime.valueBypassingBindings());
+ if (!qFuzzyCompare(lastValue, currentValue))
+ emit q->valueChanged(currentValue, QTimeLine::QPrivateSignal());
if (lastFrame != currentFrame) {
const int transitionframe = (direction == QTimeLine::Forward ? endFrame : startFrame);
if (looping && !finished && transitionframe != currentFrame) {
@@ -123,7 +125,7 @@ void QTimeLinePrivate::setCurrentTime(int msecs)
q->stop();
emit q->finished(QTimeLine::QPrivateSignal());
}
- if (currentTime.value() != previousCurrentTime)
+ if (currentTime.valueBypassingBindings() != previousCurrentTime)
currentTime.notify();
}
QBindable<int> QTimeLine::bindableCurrentTime()
@@ -334,11 +336,12 @@ QTimeLine::Direction QTimeLine::direction() const
void QTimeLine::setDirection(Direction direction)
{
Q_D(QTimeLine);
- auto previousDirection = d->direction.value();
- d->direction.setValue(direction);
+ d->direction.removeBindingUnlessInWrapper();
+ const auto previousDirection = d->direction.valueBypassingBindings();
+ d->direction.setValueBypassingBindings(direction);
d->startTime = d->currentTime;
d->timer.start();
- if (previousDirection != d->direction.value())
+ if (previousDirection != d->direction.valueBypassingBindings())
d->direction.notify();
}
@@ -372,12 +375,11 @@ void QTimeLine::setDuration(int duration)
qWarning("QTimeLine::setDuration: cannot set duration <= 0");
return;
}
- if (duration == d->duration) {
- d->duration.removeBindingUnlessInWrapper();
- return;
+ d->duration.removeBindingUnlessInWrapper();
+ if (duration != d->duration.valueBypassingBindings()) {
+ d->duration.setValueBypassingBindings(duration);
+ d->duration.notify();
}
- d->duration.setValue(duration);
- d->duration.notify();
}
QBindable<int> QTimeLine::bindableDuration()
diff --git a/src/corelib/tools/qtools_p.h b/src/corelib/tools/qtools_p.h
index 93432a9524..105aa40c02 100644
--- a/src/corelib/tools/qtools_p.h
+++ b/src/corelib/tools/qtools_p.h
@@ -115,9 +115,6 @@ constexpr inline int qt_lencmp(qsizetype lhs, qsizetype rhs) noexcept
} // namespace QtMiscUtils
-// We typically need an extra bit for qNextPowerOfTwo when determining the next allocation size.
-constexpr qsizetype MaxAllocSize = (std::numeric_limits<qsizetype>::max)();
-
struct CalculateGrowingBlockSizeResult
{
qsizetype size;
diff --git a/src/corelib/tools/qtyperevision.cpp b/src/corelib/tools/qtyperevision.cpp
new file mode 100644
index 0000000000..6426236288
--- /dev/null
+++ b/src/corelib/tools/qtyperevision.cpp
@@ -0,0 +1,217 @@
+// Copyright (C) 2021 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
+
+#include <QtCore/qtyperevision.h>
+#include <QtCore/qhashfunctions.h>
+
+#ifndef QT_NO_DATASTREAM
+# include <QtCore/qdatastream.h>
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+# include <QtCore/qdebug.h>
+#endif
+
+#include <algorithm>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+QT_IMPL_METATYPE_EXTERN(QTypeRevision)
+
+/*!
+ \class QTypeRevision
+ \inmodule QtCore
+ \since 6.0
+ \brief The QTypeRevision class contains a lightweight representation of
+ a version number with two 8-bit segments, major and minor, either
+ of which can be unknown.
+ \compares strong
+
+ Use this class to describe revisions of a type. Compatible revisions can be
+ expressed as increments of the minor version. Breaking changes can be
+ expressed as increments of the major version. The return values of
+ \l QMetaMethod::revision() and \l QMetaProperty::revision() can be passed to
+ \l QTypeRevision::fromEncodedVersion(). The resulting major and minor versions
+ specify in which Qt versions the properties and methods were added.
+
+ \sa QMetaMethod::revision(), QMetaProperty::revision()
+*/
+
+/*!
+ \fn template<typename Integer, QTypeRevision::if_valid_segment_type<Integer> = true> static bool QTypeRevision::isValidSegment(Integer segment)
+
+ Returns true if the given number can be used as either major or minor
+ version in a QTypeRevision. The valid range for \a segment is \c {>= 0} and \c {< 255}.
+*/
+
+/*!
+ \fn QTypeRevision::QTypeRevision()
+
+ Produces an invalid revision.
+
+ \sa isValid()
+*/
+
+/*!
+ \fn template<typename Major, typename Minor, QTypeRevision::if_valid_segment_type<Major> = true, QTypeRevision::if_valid_segment_type<Minor> = true> static QTypeRevision QTypeRevision::fromVersion(Major majorVersion, Minor minorVersion)
+
+ Produces a QTypeRevision from the given \a majorVersion and \a minorVersion,
+ both of which need to be a valid segments.
+
+ \sa isValidSegment()
+*/
+
+/*!
+ \fn template<typename Major, QTypeRevision::if_valid_segment_type<Major> = true> static QTypeRevision QTypeRevision::fromMajorVersion(Major majorVersion)
+
+ Produces a QTypeRevision from the given \a majorVersion with an invalid minor
+ version. \a majorVersion needs to be a valid segment.
+
+ \sa isValidSegment()
+*/
+
+/*!
+ \fn template<typename Minor, QTypeRevision::if_valid_segment_type<Minor> = true> static QTypeRevision QTypeRevision::fromMinorVersion(Minor minorVersion)
+
+ Produces a QTypeRevision from the given \a minorVersion with an invalid major
+ version. \a minorVersion needs to be a valid segment.
+
+ \sa isValidSegment()
+*/
+
+/*!
+ \fn template<typename Integer, QTypeRevision::if_valid_value_type<Integer> = true> static QTypeRevision QTypeRevision::fromEncodedVersion(Integer value)
+
+ Produces a QTypeRevision from the given \a value. \a value encodes both the
+ minor and major versions in the least significant and second least
+ significant byte, respectively.
+
+ \a value must not have any bits outside the least significant two bytes set.
+ \c Integer needs to be at least 16 bits wide, and must not have a sign bit
+ in the least significant 16 bits.
+
+ \sa toEncodedVersion()
+*/
+
+/*!
+ \fn static QTypeRevision QTypeRevision::zero()
+
+ Produces a QTypeRevision with major and minor version \c{0}.
+*/
+
+/*!
+ \fn bool QTypeRevision::hasMajorVersion() const
+
+ Returns true if the major version is known, otherwise false.
+
+ \sa majorVersion(), hasMinorVersion()
+*/
+
+/*!
+ \fn quint8 QTypeRevision::majorVersion() const
+
+ Returns the major version encoded in the revision.
+
+ \sa hasMajorVersion(), minorVersion()
+*/
+
+/*!
+ \fn bool QTypeRevision::hasMinorVersion() const
+
+ Returns true if the minor version is known, otherwise false.
+
+ \sa minorVersion(), hasMajorVersion()
+*/
+
+/*!
+ \fn quint8 QTypeRevision::minorVersion() const
+
+ Returns the minor version encoded in the revision.
+
+ \sa hasMinorVersion(), majorVersion()
+*/
+
+/*!
+ \fn bool QTypeRevision::isValid() const
+
+ Returns true if the major version or the minor version is known,
+ otherwise false.
+
+ \sa hasMajorVersion(), hasMinorVersion()
+*/
+
+/*!
+ \fn template<typename Integer, QTypeRevision::if_valid_value_type<Integer> = true> Integer QTypeRevision::toEncodedVersion() const
+
+ Transforms the revision into an integer value, encoding the minor
+ version into the least significant byte, and the major version into
+ the second least significant byte.
+
+ \c Integer needs to be at least 16 bits wide, and must not have a sign bit
+ in the least significant 16 bits.
+
+ \sa fromEncodedVersion()
+*/
+
+#ifndef QT_NO_DATASTREAM
+/*!
+ \fn QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision)
+ \relates QTypeRevision
+ \since 6.0
+
+ Writes the revision \a revision to stream \a out.
+ */
+QDataStream &operator<<(QDataStream &out, const QTypeRevision &revision)
+{
+ return out << revision.toEncodedVersion<quint16>();
+}
+
+/*!
+ \fn QDataStream& operator>>(QDataStream &in, QTypeRevision &revision)
+ \relates QTypeRevision
+ \since 6.0
+
+ Reads a revision from stream \a in and stores it in \a revision.
+ */
+QDataStream &operator>>(QDataStream &in, QTypeRevision &revision)
+{
+ quint16 value;
+ in >> value;
+ revision = QTypeRevision::fromEncodedVersion(value);
+ return in;
+}
+#endif
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QTypeRevision &revision)
+{
+ QDebugStateSaver saver(debug);
+ if (revision.hasMajorVersion()) {
+ if (revision.hasMinorVersion())
+ debug.nospace() << revision.majorVersion() << '.' << revision.minorVersion();
+ else
+ debug.nospace().noquote() << revision.majorVersion() << ".x";
+ } else {
+ if (revision.hasMinorVersion())
+ debug << revision.minorVersion();
+ else
+ debug.noquote() << "invalid";
+ }
+ return debug;
+}
+#endif
+
+/*!
+ \relates QHash
+ \since 6.0
+
+ Returns the hash value for the \a key, using \a seed to seed the
+ calculation.
+*/
+size_t qHash(const QTypeRevision &key, size_t seed)
+{
+ return qHash(key.toEncodedVersion<quint16>(), seed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/tools/qtyperevision.h b/src/corelib/tools/qtyperevision.h
new file mode 100644
index 0000000000..8f255a77e8
--- /dev/null
+++ b/src/corelib/tools/qtyperevision.h
@@ -0,0 +1,167 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2022 Intel Corporation.
+// Copyright (C) 2015 Keith Gardner <kreios4004@gmail.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTYPEREVISION_H
+#define QTYPEREVISION_H
+
+#include <QtCore/qassert.h>
+#include <QtCore/qcompare.h>
+#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qtypeinfo.h>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+class QDataStream;
+class QDebug;
+
+class QTypeRevision;
+Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0);
+
+#ifndef QT_NO_DATASTREAM
+Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
+Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
+#endif
+
+class QTypeRevision
+{
+public:
+ template<typename Integer>
+ using if_valid_segment_type = typename std::enable_if<
+ std::is_integral<Integer>::value, bool>::type;
+
+ template<typename Integer>
+ using if_valid_value_type = typename std::enable_if<
+ std::is_integral<Integer>::value
+ && (sizeof(Integer) > sizeof(quint16)
+ || (sizeof(Integer) == sizeof(quint16)
+ && !std::is_signed<Integer>::value)), bool>::type;
+
+ template<typename Integer, if_valid_segment_type<Integer> = true>
+ static constexpr bool isValidSegment(Integer segment)
+ {
+ // using extra parentheses around max to avoid expanding it if it is a macro
+ return segment >= Integer(0)
+ && ((std::numeric_limits<Integer>::max)() < Integer(SegmentUnknown)
+ || segment < Integer(SegmentUnknown));
+ }
+
+ template<typename Major, typename Minor,
+ if_valid_segment_type<Major> = true,
+ if_valid_segment_type<Minor> = true>
+ static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
+ {
+ return Q_ASSERT(isValidSegment(majorVersion)),
+ Q_ASSERT(isValidSegment(minorVersion)),
+ QTypeRevision(quint8(majorVersion), quint8(minorVersion));
+ }
+
+ template<typename Major, if_valid_segment_type<Major> = true>
+ static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
+ {
+ return Q_ASSERT(isValidSegment(majorVersion)),
+ QTypeRevision(quint8(majorVersion), SegmentUnknown);
+ }
+
+ template<typename Minor, if_valid_segment_type<Minor> = true>
+ static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
+ {
+ return Q_ASSERT(isValidSegment(minorVersion)),
+ QTypeRevision(SegmentUnknown, quint8(minorVersion));
+ }
+
+ template<typename Integer, if_valid_value_type<Integer> = true>
+ static constexpr QTypeRevision fromEncodedVersion(Integer value)
+ {
+ return Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)),
+ QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff));
+ }
+
+ static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); }
+
+ constexpr QTypeRevision() = default;
+
+ constexpr bool hasMajorVersion() const { return m_majorVersion != SegmentUnknown; }
+ constexpr quint8 majorVersion() const { return m_majorVersion; }
+
+ constexpr bool hasMinorVersion() const { return m_minorVersion != SegmentUnknown; }
+ constexpr quint8 minorVersion() const { return m_minorVersion; }
+
+ constexpr bool isValid() const { return hasMajorVersion() || hasMinorVersion(); }
+
+ template<typename Integer, if_valid_value_type<Integer> = true>
+ constexpr Integer toEncodedVersion() const
+ {
+ return Integer(m_majorVersion << 8) | Integer(m_minorVersion);
+ }
+
+private:
+ friend constexpr bool
+ comparesEqual(const QTypeRevision &lhs, const QTypeRevision &rhs) noexcept
+ { return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>(); }
+ friend constexpr Qt::strong_ordering
+ compareThreeWay(const QTypeRevision &lhs, const QTypeRevision &rhs) noexcept
+ {
+ // For both major and minor the following rule applies:
+ // non-0 ver > unspecified ver > 0 ver
+ auto cmpUnspecified = [](quint8 leftVer, quint8 rightVer) {
+ Q_ASSERT(leftVer != rightVer
+ && (leftVer == QTypeRevision::SegmentUnknown
+ || rightVer == QTypeRevision::SegmentUnknown));
+ if (leftVer != QTypeRevision::SegmentUnknown)
+ return leftVer > 0 ? Qt::strong_ordering::greater : Qt::strong_ordering::less;
+ return rightVer > 0 ? Qt::strong_ordering::less : Qt::strong_ordering::greater;
+ };
+
+ if (lhs.hasMajorVersion() != rhs.hasMajorVersion()) {
+ return cmpUnspecified(lhs.majorVersion(), rhs.majorVersion());
+ } else {
+ const auto majorRes = Qt::compareThreeWay(lhs.majorVersion(), rhs.majorVersion());
+ if (is_eq(majorRes)) {
+ if (lhs.hasMinorVersion() != rhs.hasMinorVersion())
+ return cmpUnspecified(lhs.minorVersion(), rhs.minorVersion());
+ return Qt::compareThreeWay(lhs.minorVersion(), rhs.minorVersion());
+ }
+ return majorRes;
+ }
+ }
+ Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(QTypeRevision)
+
+ enum { SegmentUnknown = 0xff };
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ constexpr QTypeRevision(quint8 major, quint8 minor)
+ : m_minorVersion(minor), m_majorVersion(major) {}
+
+ quint8 m_minorVersion = SegmentUnknown;
+ quint8 m_majorVersion = SegmentUnknown;
+#else
+ constexpr QTypeRevision(quint8 major, quint8 minor)
+ : m_majorVersion(major), m_minorVersion(minor) {}
+
+ quint8 m_majorVersion = SegmentUnknown;
+ quint8 m_minorVersion = SegmentUnknown;
+#endif
+};
+
+static_assert(sizeof(QTypeRevision) == 2);
+Q_DECLARE_TYPEINFO(QTypeRevision, Q_RELOCATABLE_TYPE);
+
+#ifndef QT_NO_DEBUG_STREAM
+Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
+#endif
+
+QT_END_NAMESPACE
+
+QT_DECL_METATYPE_EXTERN(QTypeRevision, Q_CORE_EXPORT)
+
+#endif // QTYPEREVISION_H
+
+#if !defined(QT_LEAN_HEADERS) || QT_LEAN_HEADERS < 2
+// make QVersionNumber available from <QTypeRevision>
+#include <QtCore/qversionnumber.h>
+#endif
diff --git a/src/corelib/tools/quniquehandle_p.h b/src/corelib/tools/quniquehandle_p.h
new file mode 100644
index 0000000000..7af1536c2e
--- /dev/null
+++ b/src/corelib/tools/quniquehandle_p.h
@@ -0,0 +1,225 @@
+// Copyright (C) 2023 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
+
+#ifndef QUNIQUEHANDLE_P_H
+#define QUNIQUEHANDLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qassert.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+/*! \internal QUniqueHandle is a general purpose RAII wrapper intended
+ for interfacing with resource-allocating C-style APIs, for example
+ operating system APIs, database engine APIs, or any other scenario
+ where resources are allocated and released, and where pointer
+ semantics does not seem a perfect fit.
+
+ QUniqueHandle does not support copying, because it is intended to
+ maintain ownership of resources that can not be copied. This makes
+ it safer to use than naked handle types, since ownership is
+ maintained by design.
+
+ The underlying handle object is described using a client supplied
+ HandleTraits object that is implemented per resource type. The
+ traits struct must describe two properties of a handle:
+
+ 1) What value is considered invalid
+ 2) How to close a resource.
+
+ Example 1:
+
+ struct InvalidHandleTraits {
+ using Type = HANDLE;
+
+ static Type invalidValue() {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ static bool close(Type handle) {
+ return CloseHandle(handle) != 0;
+ }
+ }
+
+ using FileHandle = QUniqueHandle<InvalidHandleTraits>;
+
+ Usage:
+
+ // Takes ownership of returned handle.
+ FileHandle handle{ CreateFile(...) };
+
+ if (!handle.isValid()) {
+ qDebug() << GetLastError()
+ return;
+ }
+
+ ...
+
+ Example 2:
+
+ struct SqLiteTraits {
+ using Type = sqlite3*;
+
+ static Type invalidValue() {
+ return nullptr;
+ }
+
+ static bool close(Type handle) {
+ sqlite3_close(handle);
+ return true;
+ }
+ }
+
+ using DbHandle = QUniqueHandle<SqLiteTraits>;
+
+ Usage:
+
+ DbHandle h;
+
+ // Take ownership of returned handle.
+ int result = sqlite3_open(":memory:", &h);
+
+ ...
+
+ NOTE: The QUniqueHandle assumes that closing a resource is
+ guaranteed to succeed, and provides no support for handling failure
+ to close a resource. It is therefore only recommended for use cases
+ where failure to close a resource is either not an error, or an
+ unrecoverable error.
+*/
+
+// clang-format off
+
+template <typename HandleTraits>
+class QUniqueHandle
+{
+public:
+ using Type = typename HandleTraits::Type;
+
+ QUniqueHandle() = default;
+
+ explicit QUniqueHandle(const Type &handle) noexcept
+ : m_handle{ handle }
+ {}
+
+ QUniqueHandle(QUniqueHandle &&other) noexcept
+ : m_handle{ other.release() }
+ {}
+
+ ~QUniqueHandle() noexcept
+ {
+ close();
+ }
+
+ QUniqueHandle& operator=(QUniqueHandle &&rhs) noexcept
+ {
+ if (this != std::addressof(rhs))
+ reset(rhs.release());
+
+ return *this;
+ }
+
+ QUniqueHandle(const QUniqueHandle &) = delete;
+ QUniqueHandle &operator=(const QUniqueHandle &) = delete;
+
+
+ [[nodiscard]] bool isValid() const noexcept
+ {
+ return m_handle != HandleTraits::invalidValue();
+ }
+
+ [[nodiscard]] explicit operator bool() const noexcept
+ {
+ return isValid();
+ }
+
+ [[nodiscard]] Type get() const noexcept
+ {
+ return m_handle;
+ }
+
+ void reset(const Type& handle) noexcept
+ {
+ if (handle == m_handle)
+ return;
+
+ close();
+ m_handle = handle;
+ }
+
+ [[nodiscard]] Type release() noexcept
+ {
+ Type handle = m_handle;
+ m_handle = HandleTraits::invalidValue();
+ return handle;
+ }
+
+ [[nodiscard]] Type *operator&() noexcept // NOLINT(google-runtime-operator)
+ {
+ Q_ASSERT(!isValid());
+ return &m_handle;
+ }
+
+ void close() noexcept
+ {
+ if (!isValid())
+ return;
+
+ const bool success = HandleTraits::close(m_handle);
+ Q_ASSERT(success);
+
+ m_handle = HandleTraits::invalidValue();
+ }
+
+ [[nodiscard]] friend bool operator==(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
+ {
+ return lhs.get() == rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator!=(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
+ {
+ return lhs.get() != rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator<(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
+ {
+ return lhs.get() < rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator<=(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
+ {
+ return lhs.get() <= rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator>(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
+ {
+ return lhs.get() > rhs.get();
+ }
+
+ [[nodiscard]] friend bool operator>=(const QUniqueHandle &lhs, const QUniqueHandle &rhs) noexcept
+ {
+ return lhs.get() >= rhs.get();
+ }
+
+private:
+ Type m_handle{ HandleTraits::invalidValue() };
+};
+
+// clang-format on
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h
index 1759fba415..78d5a27627 100644
--- a/src/corelib/tools/qvarlengtharray.h
+++ b/src/corelib/tools/qvarlengtharray.h
@@ -14,6 +14,7 @@
#include <QtCore/qalgorithms.h>
#include <QtCore/qcontainertools_impl.h>
#include <QtCore/qhashfunctions.h>
+#include <QtCore/qttypetraits.h>
#include <algorithm>
#include <initializer_list>
@@ -183,6 +184,12 @@ public:
iterator erase(const_iterator begin, const_iterator end);
iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
+ static constexpr qsizetype max_size() noexcept
+ {
+ // -1 to deal with the pointer one-past-the-end
+ return (QtPrivate::MaxAllocSize / sizeof(T)) - 1;
+ }
+
size_t hash(size_t seed) const noexcept(QtPrivate::QNothrowHashable_v<T>)
{
return qHashRange(begin(), end(), seed);
@@ -278,6 +285,8 @@ class QVarLengthArray
template <typename InputIterator>
using if_input_iterator = QtPrivate::IfIsInputIterator<InputIterator>;
public:
+ static constexpr qsizetype PreallocatedSize = Prealloc;
+
using size_type = typename Base::size_type;
using value_type = typename Base::value_type;
using pointer = typename Base::pointer;
@@ -395,6 +404,7 @@ public:
}
#ifdef Q_QDOC
inline qsizetype size() const { return this->s; }
+ static constexpr qsizetype max_size() noexcept { return QVLABase<T>::max_size(); }
#endif
using Base::size;
inline qsizetype count() const { return size(); }
@@ -823,7 +833,7 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void
qsizetype osize = size();
const qsizetype copySize = qMin(asize, osize);
- Q_ASSUME(copySize >= 0);
+ Q_ASSERT(copySize >= 0);
if (aalloc != capacity()) {
QVLABaseBase::malloced_ptr guard;
@@ -947,8 +957,8 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::insert_impl(qsizetype prealloc, void *arr
template <class T>
Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::erase(const_iterator abegin, const_iterator aend) -> iterator
{
- Q_ASSERT_X(isValidIterator(abegin), "QVarLengthArray::insert", "The specified const_iterator argument 'abegin' is invalid");
- Q_ASSERT_X(isValidIterator(aend), "QVarLengthArray::insert", "The specified const_iterator argument 'aend' is invalid");
+ Q_ASSERT_X(isValidIterator(abegin), "QVarLengthArray::erase", "The specified const_iterator argument 'abegin' is invalid");
+ Q_ASSERT_X(isValidIterator(aend), "QVarLengthArray::erase", "The specified const_iterator argument 'aend' is invalid");
qsizetype f = qsizetype(abegin - cbegin());
qsizetype l = qsizetype(aend - cbegin());
@@ -959,10 +969,11 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::erase(const_iterator abegin, const_iterat
Q_ASSERT(n > 0); // aend must be reachable from abegin
- if constexpr (QTypeInfo<T>::isComplex) {
+ if constexpr (!QTypeInfo<T>::isRelocatable) {
std::move(begin() + l, end(), QT_MAKE_CHECKED_ARRAY_ITERATOR(begin() + f, size() - f));
std::destroy(end() - n, end());
} else {
+ std::destroy(abegin, aend);
memmove(static_cast<void *>(data() + f), static_cast<const void *>(data() + l), (size() - l) * sizeof(T));
}
this->s -= n;
diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc
index 70dc41685c..4467e0c65a 100644
--- a/src/corelib/tools/qvarlengtharray.qdoc
+++ b/src/corelib/tools/qvarlengtharray.qdoc
@@ -98,12 +98,9 @@
\since 5.5
Constructs an array from the std::initializer_list given by \a args.
-
- This constructor is only enabled if the compiler supports C++11 initializer
- lists.
*/
-/*! \fn template<class T, qsizetype Prealloc> template<typename InputIterator, if_input_iterator<InputIterator>> QVarLengthArray<T, Prealloc>::QVarLengthArray(InputIterator first, InputIterator last)
+/*! \fn template<class T, qsizetype Prealloc> template<typename InputIterator, QVarLengthArray<T, Prealloc>::if_input_iterator<InputIterator>> QVarLengthArray<T, Prealloc>::QVarLengthArray(InputIterator first, InputIterator last)
\since 5.14
Constructs an array with the contents in the iterator range [\a first, \a last).
@@ -143,6 +140,15 @@
\sa isEmpty(), resize()
*/
+/*! \fn template<class T, qsizetype Prealloc> qsizetype QVarLengthArray<T, Prealloc>::max_size()
+ \since 6.8
+
+ This function is provided for STL compatibility.
+ It returns the maximum number of elements that the array can
+ theoretically hold. In practice, the number can be much smaller,
+ limited by the amount of memory available to the system.
+*/
+
/*! \fn template<class T, qsizetype Prealloc> T& QVarLengthArray<T, Prealloc>::first()
Returns a reference to the first item in the array. The array must
@@ -424,9 +430,6 @@
\since 5.5
Assigns the values of \a list to this array, and returns a reference to this array.
-
- This constructor is only enabled if the compiler supports C++11 initializer
- lists.
*/
/*! \fn template<class T, qsizetype Prealloc> QVarLengthArray<T, Prealloc>::QVarLengthArray(const QVarLengthArray<T, Prealloc> &other)
@@ -468,6 +471,15 @@
\a defaultValue.
*/
+/*
+ \var QVarLengthArray::PreallocatedSize
+ \since 6.8
+
+ The same value as the \c{Prealloc} template argument. Provided for easier
+ access compared to manually extracting the value from the template
+ argument.
+*/
+
/*!
\typedef QVarLengthArray::size_type
\since 4.7
@@ -997,7 +1009,7 @@
\sa erase()
*/
-/*! \fn template <class T, qsizetype Prealloc> QVarLengthArray<T, Prealloc>::assign(qsizetype n, const T &t)
+/*! \fn template <class T, qsizetype Prealloc> QVarLengthArray<T, Prealloc>& QVarLengthArray<T, Prealloc>::assign(qsizetype n, const T &t)
\since 6.6
Replaces the contents of this container with \a n copies of \a t.
@@ -1006,7 +1018,7 @@
allocate memory if \a n exceeds the capacity of the container.
*/
-/*! \fn template <class T, qsizetype Prealloc> template <typename InputIterator, if_input_iterator<InputIterator>> QVarLengthArray<T, Prealloc>::assign(InputIterator first, InputIterator last)
+/*! \fn template <class T, qsizetype Prealloc> template <typename InputIterator, QVarLengthArray<T, Prealloc>::if_input_iterator<InputIterator>> QVarLengthArray<T, Prealloc>& QVarLengthArray<T, Prealloc>::assign(InputIterator first, InputIterator last)
\since 6.6
Replaces the contents of this container with a copy of the elements in the
@@ -1023,7 +1035,7 @@
The behavior is undefined if either argument is an iterator into *this.
*/
-/*! \fn template <class T, qsizetype Prealloc> QVarLengthArray<T, Prealloc>::assign(std::initializer_list<T> list)
+/*! \fn template <class T, qsizetype Prealloc> QVarLengthArray<T, Prealloc>& QVarLengthArray<T, Prealloc>::assign(std::initializer_list<T> list)
\since 6.6
Replaces the contents of this container with a copy of the elements of \a list.
diff --git a/src/corelib/tools/qversionnumber.cpp b/src/corelib/tools/qversionnumber.cpp
index dcb2a3ad64..af95875b44 100644
--- a/src/corelib/tools/qversionnumber.cpp
+++ b/src/corelib/tools/qversionnumber.cpp
@@ -22,7 +22,6 @@
QT_BEGIN_NAMESPACE
QT_IMPL_METATYPE_EXTERN(QVersionNumber)
-QT_IMPL_METATYPE_EXTERN(QTypeRevision)
/*!
\class QVersionNumber
@@ -31,6 +30,7 @@ QT_IMPL_METATYPE_EXTERN(QTypeRevision)
\brief The QVersionNumber class contains a version number with an arbitrary
number of segments.
+ \compares strong
\snippet qversionnumber/main.cpp 0
*/
@@ -72,25 +72,23 @@ QT_IMPL_METATYPE_EXTERN(QTypeRevision)
\fn QVersionNumber::QVersionNumber(QList<int> &&seg)
Move-constructs a version number from the list of numbers contained in \a seg.
-
- This constructor is only enabled if the compiler supports C++11 move semantics.
*/
/*!
\fn QVersionNumber::QVersionNumber(std::initializer_list<int> args)
- Construct a version number from the std::initializer_list specified by
+ Constructs a version number from the std::initializer_list specified by
\a args.
-
- This constructor is only enabled if the compiler supports C++11 initializer
- lists.
*/
/*!
- \fn template <qsizetype N> QVersionNumber::QVersionNumber(const QVarLengthArray<int, N> &seg)
- \since 6.4
+ \fn QVersionNumber::QVersionNumber(QSpan<const int> args)
+ \since 6.8
- Constructs a version number from the list of numbers contained in \a seg.
+ Constructs a version number from the span specified by \a args.
+
+ \note In Qt versions prior to 6.8, QVersionNumber could only be constructed
+ from QList, QVarLenthArray or std::initializer_list.
*/
/*!
@@ -178,6 +176,55 @@ QList<int> QVersionNumber::segments() const
*/
/*!
+ \typedef QVersionNumber::const_iterator
+ \typedef QVersionNumber::const_reverse_iterator
+ \since 6.8
+
+ Typedefs for an opaque class that implements a (reverse) random-access
+ iterator over QVersionNumber segments.
+
+ \note QVersionNumber does not support modifying segments in-place, so
+ there is no mutable iterator.
+*/
+
+/*!
+ \typedef QVersionNumber::value_type
+ \typedef QVersionNumber::difference_type
+ \typedef QVersionNumber::size_type
+ \typedef QVersionNumber::reference
+ \typedef QVersionNumber::const_reference
+ \typedef QVersionNumber::pointer
+ \typedef QVersionNumber::const_pointer
+ \since 6.8
+
+ Provided for STL-compatibility.
+
+ \note QVersionNumber does not support modifying segments in-place, so
+ reference and const_reference, as well as pointer and const_pointer are
+ pairwise the same types.
+*/
+
+/*!
+ \fn QVersionNumber::begin() const
+ \fn QVersionNumber::end() const;
+ \fn QVersionNumber::rbegin() const
+ \fn QVersionNumber::rend() const;
+ \fn QVersionNumber::cbegin() const
+ \fn QVersionNumber::cend() const;
+ \fn QVersionNumber::crbegin() const
+ \fn QVersionNumber::crend() const;
+ \fn QVersionNumber::constBegin() const;
+ \fn QVersionNumber::constEnd() const;
+ \since 6.8
+
+ Returns a const_iterator or const_reverse_iterator, respectively, pointing
+ to the first or one past the last segment of this version number.
+
+ \note QVersionNumber does not support modifying segments in-place, so
+ there is no mutable iterator.
+*/
+
+/*!
\fn QVersionNumber QVersionNumber::normalized() const
Returns an equivalent version number but with all trailing zeros removed.
@@ -538,198 +585,4 @@ size_t qHash(const QVersionNumber &key, size_t seed)
return seed;
}
-/*!
- \class QTypeRevision
- \inmodule QtCore
- \since 6.0
- \brief The QTypeRevision class contains a lightweight representation of
- a version number with two 8-bit segments, major and minor, either
- of which can be unknown.
-
- Use this class to describe revisions of a type. Compatible revisions can be
- expressed as increments of the minor version. Breaking changes can be
- expressed as increments of the major version. The return values of
- \l QMetaMethod::revision() and \l QMetaProperty::revision() can be passed to
- \l QTypeRevision::fromEncodedVersion(). The resulting major and minor versions
- specify in which Qt versions the properties and methods were added.
-
- \sa QMetaMethod::revision(), QMetaProperty::revision()
-*/
-
-/*!
- \fn template<typename Integer> static bool QTypeRevision::isValidSegment(Integer segment)
-
- Returns true if the given number can be used as either major or minor
- version in a QTypeRevision. The valid range for \a segment is \c {>= 0} and \c {< 255}.
-*/
-
-/*!
- \fn QTypeRevision::QTypeRevision()
-
- Produces an invalid revision.
-
- \sa isValid()
-*/
-
-/*!
- \fn template <typename Major, typename Minor> static QTypeRevision QTypeRevision::fromVersion(Major majorVersion, Minor minorVersion)
-
- Produces a QTypeRevision from the given \a majorVersion and \a minorVersion,
- both of which need to be a valid segments.
-
- \sa isValidSegment()
-*/
-
-/*!
- \fn template <typename Major> static QTypeRevision QTypeRevision::fromMajorVersion(Major majorVersion)
-
- Produces a QTypeRevision from the given \a majorVersion with an invalid minor
- version. \a majorVersion needs to be a valid segment.
-
- \sa isValidSegment()
-*/
-
-/*!
- \fn template <typename Minor> static QTypeRevision QTypeRevision::fromMinorVersion(Minor minorVersion)
-
- Produces a QTypeRevision from the given \a minorVersion with an invalid major
- version. \a minorVersion needs to be a valid segment.
-
- \sa isValidSegment()
-*/
-
-/*!
- \fn template <typename Integer> static QTypeRevision QTypeRevision::fromEncodedVersion(Integer value)
-
- Produces a QTypeRevision from the given \a value. \a value encodes both the
- minor and major versions in the least significant and second least
- significant byte, respectively.
-
- \a value must not have any bits outside the least significant two bytes set.
- \c Integer needs to be at least 16 bits wide, and must not have a sign bit
- in the least significant 16 bits.
-
- \sa toEncodedVersion()
-*/
-
-/*!
- \fn static QTypeRevision QTypeRevision::zero()
-
- Produces a QTypeRevision with major and minor version \c{0}.
-*/
-
-/*!
- \fn bool QTypeRevision::hasMajorVersion() const
-
- Returns true if the major version is known, otherwise false.
-
- \sa majorVersion(), hasMinorVersion()
-*/
-
-/*!
- \fn quint8 QTypeRevision::majorVersion() const
-
- Returns the major version encoded in the revision.
-
- \sa hasMajorVersion(), minorVersion()
-*/
-
-/*!
- \fn bool QTypeRevision::hasMinorVersion() const
-
- Returns true if the minor version is known, otherwise false.
-
- \sa minorVersion(), hasMajorVersion()
-*/
-
-/*!
- \fn quint8 QTypeRevision::minorVersion() const
-
- Returns the minor version encoded in the revision.
-
- \sa hasMinorVersion(), majorVersion()
-*/
-
-/*!
- \fn bool QTypeRevision::isValid() const
-
- Returns true if the major version or the minor version is known,
- otherwise false.
-
- \sa hasMajorVersion(), hasMinorVersion()
-*/
-
-/*!
- \fn template<typename Integer> Integer QTypeRevision::toEncodedVersion() const
-
- Transforms the revision into an integer value, encoding the minor
- version into the least significant byte, and the major version into
- the second least significant byte.
-
- \c Integer needs to be at least 16 bits wide, and must not have a sign bit
- in the least significant 16 bits.
-
- \sa fromEncodedVersion()
-*/
-
-#ifndef QT_NO_DATASTREAM
-/*!
- \fn QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision)
- \relates QTypeRevision
- \since 6.0
-
- Writes the revision \a revision to stream \a out.
- */
-QDataStream &operator<<(QDataStream &out, const QTypeRevision &revision)
-{
- return out << revision.toEncodedVersion<quint16>();
-}
-
-/*!
- \fn QDataStream& operator>>(QDataStream &in, QTypeRevision &revision)
- \relates QTypeRevision
- \since 6.0
-
- Reads a revision from stream \a in and stores it in \a revision.
- */
-QDataStream &operator>>(QDataStream &in, QTypeRevision &revision)
-{
- quint16 value;
- in >> value;
- revision = QTypeRevision::fromEncodedVersion(value);
- return in;
-}
-#endif
-
-#ifndef QT_NO_DEBUG_STREAM
-QDebug operator<<(QDebug debug, const QTypeRevision &revision)
-{
- QDebugStateSaver saver(debug);
- if (revision.hasMajorVersion()) {
- if (revision.hasMinorVersion())
- debug.nospace() << revision.majorVersion() << '.' << revision.minorVersion();
- else
- debug.nospace().noquote() << revision.majorVersion() << ".x";
- } else {
- if (revision.hasMinorVersion())
- debug << revision.minorVersion();
- else
- debug.noquote() << "invalid";
- }
- return debug;
-}
-#endif
-
-/*!
- \relates QHash
- \since 6.0
-
- Returns the hash value for the \a key, using \a seed to seed the
- calculation.
-*/
-size_t qHash(const QTypeRevision &key, size_t seed)
-{
- return qHash(key.toEncodedVersion<quint16>(), seed);
-}
-
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qversionnumber.h b/src/corelib/tools/qversionnumber.h
index a7c2b44a7e..e7ae107226 100644
--- a/src/corelib/tools/qversionnumber.h
+++ b/src/corelib/tools/qversionnumber.h
@@ -6,12 +6,17 @@
#ifndef QVERSIONNUMBER_H
#define QVERSIONNUMBER_H
+#include <QtCore/qcompare.h>
+#include <QtCore/qcontainertools_impl.h>
#include <QtCore/qlist.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qnamespace.h>
+#include <QtCore/qspan.h>
#include <QtCore/qstring.h>
#include <QtCore/qtypeinfo.h>
-#include <limits>
+#if !defined(QT_LEAN_HEADERS) || QT_LEAN_HEADERS < 2
+#include <QtCore/qtyperevision.h>
+#endif // lean headers level 2
QT_BEGIN_NAMESPACE
@@ -110,7 +115,7 @@ class QVersionNumber
Q_CORE_EXPORT void setListData(QList<int> &&seg);
- explicit SegmentStorage(std::initializer_list<int> args)
+ explicit SegmentStorage(QSpan<const int> args)
: SegmentStorage(args.begin(), args.end()) {}
explicit SegmentStorage(const int *first, const int *last)
@@ -188,23 +193,85 @@ class QVersionNumber
Q_CORE_EXPORT void setVector(int len, int maj, int min, int mic);
} m_segments;
+ class It
+ {
+ const QVersionNumber *v;
+ qsizetype i;
+
+ friend class QVersionNumber;
+ explicit constexpr It(const QVersionNumber *vn, qsizetype idx) noexcept : v(vn), i(idx) {}
+
+ friend constexpr bool comparesEqual(const It &lhs, const It &rhs)
+ { Q_ASSERT(lhs.v == rhs.v); return lhs.i == rhs.i; }
+ friend constexpr Qt::strong_ordering compareThreeWay(const It &lhs, const It &rhs)
+ { Q_ASSERT(lhs.v == rhs.v); return Qt::compareThreeWay(lhs.i, rhs.i); }
+ Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(It)
+
+ public:
+ // Rule Of Zero applies
+ It() = default;
+
+ using iterator_category = std::random_access_iterator_tag;
+ using value_type = int;
+#ifdef QT_COMPILER_HAS_LWG3346
+ using element_type = const int;
+#endif
+ using difference_type = qptrdiff; // difference to container requirements
+ using size_type = qsizetype; // difference to container requirements
+ using reference = value_type; // difference to container requirements
+ using pointer = QtPrivate::ArrowProxy<reference>;
+
+ reference operator*() const { return v->segmentAt(i); }
+ pointer operator->() const { return {**this}; }
+
+ It &operator++() { ++i; return *this; }
+ It operator++(int) { auto copy = *this; ++*this; return copy; }
+
+ It &operator--() { --i; return *this; }
+ It operator--(int) { auto copy = *this; --*this; return copy; }
+
+ It &operator+=(difference_type n) { i += n; return *this; }
+ friend It operator+(It it, difference_type n) { it += n; return it; }
+ friend It operator+(difference_type n, It it) { return it + n; }
+
+ It &operator-=(difference_type n) { i -= n; return *this; }
+ friend It operator-(It it, difference_type n) { it -= n; return it; }
+
+ friend difference_type operator-(It lhs, It rhs)
+ { Q_ASSERT(lhs.v == rhs.v); return lhs.i - rhs.i; }
+
+ reference operator[](difference_type n) const { return *(*this + n); }
+ };
+
public:
+ using const_iterator = It;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ using value_type = It::value_type;
+ using difference_type = It::difference_type;
+ using size_type = It::size_type;
+ using reference = It::reference;
+ using const_reference = reference;
+ using pointer = It::pointer;
+ using const_pointer = pointer;
+
inline QVersionNumber() noexcept
: m_segments()
{}
+ Q_WEAK_OVERLOAD
inline explicit QVersionNumber(const QList<int> &seg) : m_segments(seg) { }
// compiler-generated copy/move ctor/assignment operators and the destructor are ok
+ Q_WEAK_OVERLOAD
explicit QVersionNumber(QList<int> &&seg) : m_segments(std::move(seg)) { }
inline QVersionNumber(std::initializer_list<int> args)
- : m_segments(args)
+ : m_segments(QSpan{args})
{}
- template <qsizetype N>
- explicit QVersionNumber(const QVarLengthArray<int, N> &sec)
- : m_segments(sec.begin(), sec.end())
+ explicit QVersionNumber(QSpan<const int> args)
+ : m_segments(args)
{}
inline explicit QVersionNumber(int maj)
@@ -241,6 +308,19 @@ public:
[[nodiscard]] inline qsizetype segmentCount() const noexcept
{ return m_segments.size(); }
+ [[nodiscard]] const_iterator begin() const noexcept { return const_iterator{this, 0}; }
+ [[nodiscard]] const_iterator end() const noexcept { return begin() + segmentCount(); }
+ [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); }
+ [[nodiscard]] const_iterator cend() const noexcept { return end(); }
+
+ [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator{end()}; }
+ [[nodiscard]] const_reverse_iterator rend() const noexcept { return const_reverse_iterator{begin()}; }
+ [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); }
+ [[nodiscard]] const_reverse_iterator crend() const noexcept { return rend(); }
+
+ [[nodiscard]] const_iterator constBegin() const noexcept { return begin(); }
+ [[nodiscard]] const_iterator constEnd() const noexcept { return end(); }
+
[[nodiscard]] Q_CORE_EXPORT bool isPrefixOf(const QVersionNumber &other) const noexcept;
[[nodiscard]] Q_CORE_EXPORT static int compare(const QVersionNumber &v1, const QVersionNumber &v2) noexcept;
@@ -276,191 +356,34 @@ public:
[[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(QStringView string, int *suffixIndex);
#endif
- [[nodiscard]] friend bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) > 0; }
-
- [[nodiscard]] friend bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) >= 0; }
-
- [[nodiscard]] friend bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) < 0; }
-
- [[nodiscard]] friend bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) <= 0; }
-
- [[nodiscard]] friend bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) == 0; }
-
- [[nodiscard]] friend bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) != 0; }
-
private:
-#ifndef QT_NO_DATASTREAM
- friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
-#endif
- friend Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed);
-};
-
-Q_DECLARE_TYPEINFO(QVersionNumber, Q_RELOCATABLE_TYPE);
-
-#ifndef QT_NO_DEBUG_STREAM
-Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version);
-#endif
-
-class QTypeRevision;
-Q_CORE_EXPORT size_t qHash(const QTypeRevision &key, size_t seed = 0);
-
-#ifndef QT_NO_DATASTREAM
-Q_CORE_EXPORT QDataStream& operator<<(QDataStream &out, const QTypeRevision &revision);
-Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QTypeRevision &revision);
-#endif
-
-class QTypeRevision
-{
-public:
- template<typename Integer>
- using if_valid_segment_type = typename std::enable_if<
- std::is_integral<Integer>::value, bool>::type;
-
- template<typename Integer>
- using if_valid_value_type = typename std::enable_if<
- std::is_integral<Integer>::value
- && (sizeof(Integer) > sizeof(quint16)
- || (sizeof(Integer) == sizeof(quint16)
- && !std::is_signed<Integer>::value)), bool>::type;
-
- template<typename Integer, if_valid_segment_type<Integer> = true>
- static constexpr bool isValidSegment(Integer segment)
+ [[nodiscard]] friend bool comparesEqual(const QVersionNumber &lhs,
+ const QVersionNumber &rhs) noexcept
{
- // using extra parentheses around max to avoid expanding it if it is a macro
- return segment >= Integer(0)
- && ((std::numeric_limits<Integer>::max)() < Integer(SegmentUnknown)
- || segment < Integer(SegmentUnknown));
+ return compare(lhs, rhs) == 0;
}
-
- template<typename Major, typename Minor,
- if_valid_segment_type<Major> = true,
- if_valid_segment_type<Minor> = true>
- static constexpr QTypeRevision fromVersion(Major majorVersion, Minor minorVersion)
+ [[nodiscard]] friend Qt::strong_ordering compareThreeWay(const QVersionNumber &lhs,
+ const QVersionNumber &rhs) noexcept
{
- return Q_ASSERT(isValidSegment(majorVersion)),
- Q_ASSERT(isValidSegment(minorVersion)),
- QTypeRevision(quint8(majorVersion), quint8(minorVersion));
+ int c = compare(lhs, rhs);
+ return Qt::compareThreeWay(c, 0);
}
+ Q_DECLARE_STRONGLY_ORDERED(QVersionNumber)
- template<typename Major, if_valid_segment_type<Major> = true>
- static constexpr QTypeRevision fromMajorVersion(Major majorVersion)
- {
- return Q_ASSERT(isValidSegment(majorVersion)),
- QTypeRevision(quint8(majorVersion), SegmentUnknown);
- }
-
- template<typename Minor, if_valid_segment_type<Minor> = true>
- static constexpr QTypeRevision fromMinorVersion(Minor minorVersion)
- {
- return Q_ASSERT(isValidSegment(minorVersion)),
- QTypeRevision(SegmentUnknown, quint8(minorVersion));
- }
-
- template<typename Integer, if_valid_value_type<Integer> = true>
- static constexpr QTypeRevision fromEncodedVersion(Integer value)
- {
- return Q_ASSERT((value & ~Integer(0xffff)) == Integer(0)),
- QTypeRevision((value & Integer(0xff00)) >> 8, value & Integer(0xff));
- }
-
- static constexpr QTypeRevision zero() { return QTypeRevision(0, 0); }
-
- constexpr QTypeRevision() = default;
-
- constexpr bool hasMajorVersion() const { return m_majorVersion != SegmentUnknown; }
- constexpr quint8 majorVersion() const { return m_majorVersion; }
-
- constexpr bool hasMinorVersion() const { return m_minorVersion != SegmentUnknown; }
- constexpr quint8 minorVersion() const { return m_minorVersion; }
-
- constexpr bool isValid() const { return hasMajorVersion() || hasMinorVersion(); }
-
- template<typename Integer, if_valid_value_type<Integer> = true>
- constexpr Integer toEncodedVersion() const
- {
- return Integer(m_majorVersion << 8) | Integer(m_minorVersion);
- }
-
- [[nodiscard]] friend constexpr bool operator==(QTypeRevision lhs, QTypeRevision rhs)
- {
- return lhs.toEncodedVersion<quint16>() == rhs.toEncodedVersion<quint16>();
- }
-
- [[nodiscard]] friend constexpr bool operator!=(QTypeRevision lhs, QTypeRevision rhs)
- {
- return lhs.toEncodedVersion<quint16>() != rhs.toEncodedVersion<quint16>();
- }
-
- [[nodiscard]] friend constexpr bool operator<(QTypeRevision lhs, QTypeRevision rhs)
- {
- return (!lhs.hasMajorVersion() && rhs.hasMajorVersion())
- // non-0 major > unspecified major > major 0
- ? rhs.majorVersion() != 0
- : ((lhs.hasMajorVersion() && !rhs.hasMajorVersion())
- // major 0 < unspecified major < non-0 major
- ? lhs.majorVersion() == 0
- : (lhs.majorVersion() != rhs.majorVersion()
- // both majors specified and non-0
- ? lhs.majorVersion() < rhs.majorVersion()
- : ((!lhs.hasMinorVersion() && rhs.hasMinorVersion())
- // non-0 minor > unspecified minor > minor 0
- ? rhs.minorVersion() != 0
- : ((lhs.hasMinorVersion() && !rhs.hasMinorVersion())
- // minor 0 < unspecified minor < non-0 minor
- ? lhs.minorVersion() == 0
- // both minors specified and non-0
- : lhs.minorVersion() < rhs.minorVersion()))));
- }
-
- [[nodiscard]] friend constexpr bool operator>(QTypeRevision lhs, QTypeRevision rhs)
- {
- return lhs != rhs && !(lhs < rhs);
- }
-
- [[nodiscard]] friend constexpr bool operator<=(QTypeRevision lhs, QTypeRevision rhs)
- {
- return lhs == rhs || lhs < rhs;
- }
-
- [[nodiscard]] friend constexpr bool operator>=(QTypeRevision lhs, QTypeRevision rhs)
- {
- return lhs == rhs || !(lhs < rhs);
- }
-
-private:
- enum { SegmentUnknown = 0xff };
-
-#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- constexpr QTypeRevision(quint8 major, quint8 minor)
- : m_minorVersion(minor), m_majorVersion(major) {}
-
- quint8 m_minorVersion = SegmentUnknown;
- quint8 m_majorVersion = SegmentUnknown;
-#else
- constexpr QTypeRevision(quint8 major, quint8 minor)
- : m_majorVersion(major), m_minorVersion(minor) {}
-
- quint8 m_majorVersion = SegmentUnknown;
- quint8 m_minorVersion = SegmentUnknown;
+#ifndef QT_NO_DATASTREAM
+ friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
#endif
+ friend Q_CORE_EXPORT size_t qHash(const QVersionNumber &key, size_t seed);
};
-static_assert(sizeof(QTypeRevision) == 2);
-Q_DECLARE_TYPEINFO(QTypeRevision, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QVersionNumber, Q_RELOCATABLE_TYPE);
#ifndef QT_NO_DEBUG_STREAM
-Q_CORE_EXPORT QDebug operator<<(QDebug, const QTypeRevision &revision);
+Q_CORE_EXPORT QDebug operator<<(QDebug, const QVersionNumber &version);
#endif
QT_END_NAMESPACE
QT_DECL_METATYPE_EXTERN(QVersionNumber, Q_CORE_EXPORT)
-QT_DECL_METATYPE_EXTERN(QTypeRevision, Q_CORE_EXPORT)
#endif // QVERSIONNUMBER_H