summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools')
-rw-r--r--src/corelib/tools/qalgorithms.h25
-rw-r--r--src/corelib/tools/qalgorithms.qdoc12
-rw-r--r--src/corelib/tools/qarraydata.cpp128
-rw-r--r--src/corelib/tools/qarraydata.h72
-rw-r--r--src/corelib/tools/qarraydataops.h71
-rw-r--r--src/corelib/tools/qarraydatapointer.h141
-rw-r--r--src/corelib/tools/qatomicscopedvaluerollback.h127
-rw-r--r--src/corelib/tools/qatomicscopedvaluerollback.qdoc123
-rw-r--r--src/corelib/tools/qbitarray.cpp360
-rw-r--r--src/corelib/tools/qbitarray.h139
-rw-r--r--src/corelib/tools/qcommandlineoption.cpp6
-rw-r--r--src/corelib/tools/qcommandlineparser.cpp130
-rw-r--r--src/corelib/tools/qcontainerfwd.h29
-rw-r--r--src/corelib/tools/qcontainertools_impl.h150
-rw-r--r--src/corelib/tools/qcontiguouscache.cpp2
-rw-r--r--src/corelib/tools/qcontiguouscache.h12
-rw-r--r--src/corelib/tools/qcryptographichash.cpp1130
-rw-r--r--src/corelib/tools/qcryptographichash.h7
-rw-r--r--src/corelib/tools/qduplicatetracker_p.h6
-rw-r--r--src/corelib/tools/qeasingcurve.cpp24
-rw-r--r--src/corelib/tools/qflatmap_p.h46
-rw-r--r--src/corelib/tools/qfreelist.cpp10
-rw-r--r--src/corelib/tools/qfreelist_p.h3
-rw-r--r--src/corelib/tools/qfunctionaltools_impl.cpp47
-rw-r--r--src/corelib/tools/qfunctionaltools_impl.h77
-rw-r--r--src/corelib/tools/qhash.cpp722
-rw-r--r--src/corelib/tools/qhash.h524
-rw-r--r--src/corelib/tools/qhashfunctions.h126
-rw-r--r--src/corelib/tools/qiterator.h44
-rw-r--r--src/corelib/tools/qiterator.qdoc64
-rw-r--r--src/corelib/tools/qline.h4
-rw-r--r--src/corelib/tools/qlist.h114
-rw-r--r--src/corelib/tools/qlist.qdoc156
-rw-r--r--src/corelib/tools/qmap.h43
-rw-r--r--src/corelib/tools/qmap.qdoc137
-rw-r--r--src/corelib/tools/qmargins.cpp2
-rw-r--r--src/corelib/tools/qmargins.h25
-rw-r--r--src/corelib/tools/qmessageauthenticationcode.cpp247
-rw-r--r--src/corelib/tools/qmessageauthenticationcode.h27
-rw-r--r--src/corelib/tools/qminimalflatset_p.h156
-rw-r--r--src/corelib/tools/qmultimap.qdoc88
-rw-r--r--src/corelib/tools/qoffsetstringarray_p.h42
-rw-r--r--src/corelib/tools/qpair.h7
-rw-r--r--src/corelib/tools/qpoint.h17
-rw-r--r--src/corelib/tools/qrect.h10
-rw-r--r--src/corelib/tools/qringbuffer.cpp9
-rw-r--r--src/corelib/tools/qscopedpointer.cpp12
-rw-r--r--src/corelib/tools/qscopedpointer.h15
-rw-r--r--src/corelib/tools/qscopedvaluerollback.h7
-rw-r--r--src/corelib/tools/qscopeguard.h7
-rw-r--r--src/corelib/tools/qset.h37
-rw-r--r--src/corelib/tools/qset.qdoc34
-rw-r--r--src/corelib/tools/qshareddata.h23
-rw-r--r--src/corelib/tools/qshareddata_impl.h4
-rw-r--r--src/corelib/tools/qsharedpointer.cpp144
-rw-r--r--src/corelib/tools/qsharedpointer.h34
-rw-r--r--src/corelib/tools/qsharedpointer_impl.h93
-rw-r--r--src/corelib/tools/qsize.h17
-rw-r--r--src/corelib/tools/qspan.h452
-rw-r--r--src/corelib/tools/qspan.qdoc651
-rw-r--r--src/corelib/tools/qspan_p.h24
-rw-r--r--src/corelib/tools/qtaggedpointer.h37
-rw-r--r--src/corelib/tools/qtaggedpointer.qdoc2
-rw-r--r--src/corelib/tools/qtimeline.cpp44
-rw-r--r--src/corelib/tools/qtools_p.h66
-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.h257
-rw-r--r--src/corelib/tools/qvarlengtharray.qdoc67
-rw-r--r--src/corelib/tools/qversionnumber.cpp275
-rw-r--r--src/corelib/tools/qversionnumber.h243
72 files changed, 6586 insertions, 2009 deletions
diff --git a/src/corelib/tools/qalgorithms.h b/src/corelib/tools/qalgorithms.h
index f72cd669cb..8889effece 100644
--- a/src/corelib/tools/qalgorithms.h
+++ b/src/corelib/tools/qalgorithms.h
@@ -4,6 +4,10 @@
#ifndef QALGORITHMS_H
#define QALGORITHMS_H
+#if 0
+#pragma qt_class(QtAlgorithms)
+#endif
+
#include <QtCore/qglobal.h>
#if __has_include(<bit>) && __cplusplus > 201703L
@@ -37,19 +41,11 @@ inline void qDeleteAll(const Container &c)
*/
namespace QAlgorithmsPrivate {
-#ifdef Q_CC_CLANG
-// Clang had a bug where __builtin_ctz/clz/popcount were not marked as constexpr.
-# if (defined __apple_build_version__ && __clang_major__ >= 7) || (Q_CC_CLANG >= 307)
-# define QT_HAS_CONSTEXPR_BUILTINS
-# endif
-#elif defined(Q_CC_MSVC) && !defined(Q_PROCESSOR_ARM)
-# define QT_HAS_CONSTEXPR_BUILTINS
+#if defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L
+// We use C++20 <bit> operations instead which ensures constexpr bit ops
+# define QT_HAS_CONSTEXPR_BITOPS
#elif defined(Q_CC_GNU)
-# define QT_HAS_CONSTEXPR_BUILTINS
-#endif
-
-#if defined QT_HAS_CONSTEXPR_BUILTINS
-#if defined(Q_CC_GNU) || defined(Q_CC_CLANG)
+# define QT_HAS_CONSTEXPR_BITOPS
# define QT_HAS_BUILTIN_CTZS
constexpr Q_ALWAYS_INLINE uint qt_builtin_ctzs(quint16 v) noexcept
{
@@ -165,9 +161,7 @@ Q_ALWAYS_INLINE uint qt_builtin_clzs(quint16 v) noexcept
// architecture), but unlike the other compilers, MSVC has no option
// to generate code for those processors.
// So it's an acceptable compromise.
-#if defined(__cpp_lib_bitops) && __cpp_lib_bitops >= 201907L
-// We use C++20 <bit> operations instead which ensures constexpr popcount
-#elif defined(__AVX__) || defined(__SSE4_2__) || defined(__POPCNT__)
+#if defined(__AVX__) || defined(__SSE4_2__) || defined(__POPCNT__)
#define QT_POPCOUNT_CONSTEXPR
#define QT_POPCOUNT_RELAXED_CONSTEXPR
#define QALGORITHMS_USE_BUILTIN_POPCOUNT
@@ -196,7 +190,6 @@ Q_ALWAYS_INLINE uint qt_builtin_popcountll(quint64 v) noexcept
#endif // __AVX__ || __SSE4_2__ || __POPCNT__
#endif // MSVC
-#endif // QT_HAS_CONSTEXPR_BUILTINS
#ifndef QT_POPCOUNT_CONSTEXPR
#define QT_POPCOUNT_CONSTEXPR constexpr
diff --git a/src/corelib/tools/qalgorithms.qdoc b/src/corelib/tools/qalgorithms.qdoc
index 4a9c03a43c..5fe0b3e8c3 100644
--- a/src/corelib/tools/qalgorithms.qdoc
+++ b/src/corelib/tools/qalgorithms.qdoc
@@ -109,18 +109,6 @@
\sa {container classes}, <QtGlobal>
*/
-/*! \fn template <typename T> void qSwap(T &var1, T &var2)
- \relates <QtAlgorithms>
- \deprecated
-
- Use \c std::swap instead.
-
- Exchanges the values of variables \a var1 and \a var2.
-
- Example:
- \snippet code/doc_src_qalgorithms.cpp 0
-*/
-
/*!
\fn template <typename ForwardIterator> void qDeleteAll(ForwardIterator begin, ForwardIterator end)
\relates <QtAlgorithms>
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 afd1a264bd..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>
@@ -35,6 +36,8 @@ protected:
public:
typedef typename QArrayDataPointer<T>::parameter_type parameter_type;
+ using QArrayDataPointer<T>::QArrayDataPointer;
+
void appendInitialize(qsizetype newSize) noexcept
{
Q_ASSERT(this->isMutable());
@@ -213,6 +216,51 @@ public:
--this->size;
}
+ template <typename Predicate>
+ qsizetype eraseIf(Predicate pred)
+ {
+ qsizetype result = 0;
+ if (this->size == 0)
+ return result;
+
+ if (!this->needsDetach()) {
+ auto end = this->end();
+ auto it = std::remove_if(this->begin(), end, pred);
+ if (it != end) {
+ result = std::distance(it, end);
+ erase(it, result);
+ }
+ } else {
+ const auto begin = this->begin();
+ const auto end = this->end();
+ auto it = std::find_if(begin, end, pred);
+ if (it == end)
+ return result;
+
+ QPodArrayOps<T> other(this->size);
+ Q_CHECK_PTR(other.data());
+ auto dest = other.begin();
+ // std::uninitialized_copy will fallback to ::memcpy/memmove()
+ dest = std::uninitialized_copy(begin, it, dest);
+ dest = q_uninitialized_remove_copy_if(std::next(it), end, dest, pred);
+ other.size = std::distance(other.data(), dest);
+ result = this->size - other.size;
+ this->swap(other);
+ }
+ return result;
+ }
+
+ struct Span { T *begin; T *end; };
+
+ void copyRanges(std::initializer_list<Span> ranges)
+ {
+ auto it = this->begin();
+ std::for_each(ranges.begin(), ranges.end(), [&it](const auto &span) {
+ it = std::copy(span.begin, span.end, it);
+ });
+ this->size = std::distance(this->begin(), it);
+ }
+
void assign(T *b, T *e, parameter_type t) noexcept
{
Q_ASSERT(b <= e);
@@ -905,15 +953,32 @@ public:
DataPointer old;
// points into range:
- if (QtPrivate::q_points_into_range(b, this->begin(), this->end())) {
+ if (QtPrivate::q_points_into_range(b, *this))
this->detachAndGrow(QArrayData::GrowsAtEnd, n, &b, &old);
- } else {
+ else
this->detachAndGrow(QArrayData::GrowsAtEnd, n, nullptr, nullptr);
- }
Q_ASSERT(this->freeSpaceAtEnd() >= n);
// 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 3e1c2c11e4..6657d40cf9 100644
--- a/src/corelib/tools/qarraydatapointer.h
+++ b/src/corelib/tools/qarraydatapointer.h
@@ -7,6 +7,9 @@
#include <QtCore/qarraydataops.h>
#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/q20functional.h>
+#include <QtCore/q20memory.h>
+
QT_BEGIN_NAMESPACE
template <class T>
@@ -24,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);
@@ -58,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)
@@ -92,7 +107,7 @@ public:
{
if (!deref()) {
(*this)->destroyAll();
- Data::deallocate(d);
+ free(d);
}
}
@@ -300,11 +315,123 @@ public:
T *res = this->ptr + offset;
QtPrivate::q_relocate_overlap_n(this->ptr, this->size, res);
// first update data pointer, then this->ptr
- if (data && QtPrivate::q_points_into_range(*data, this->begin(), this->end()))
+ if (data && QtPrivate::q_points_into_range(*data, *this))
*data += offset;
this->ptr = res;
}
+ template <typename InputIterator, typename Projection = q20::identity>
+ void assign(InputIterator first, InputIterator last, Projection proj = {})
+ {
+ // This function only provides the basic exception guarantee.
+ constexpr bool IsFwdIt = std::is_convertible_v<
+ typename std::iterator_traits<InputIterator>::iterator_category,
+ std::forward_iterator_tag>;
+ constexpr bool IsIdentity = std::is_same_v<Projection, q20::identity>;
+
+ if constexpr (IsFwdIt) {
+ const qsizetype n = std::distance(first, last);
+ if (needsDetach() || n > constAllocatedCapacity()) {
+ QArrayDataPointer allocated(detachCapacity(n));
+ swap(allocated);
+ }
+ } else if (needsDetach()) {
+ QArrayDataPointer allocated(allocatedCapacity());
+ swap(allocated);
+ // We don't want to copy data that we know we'll overwrite
+ }
+
+ auto offset = freeSpaceAtBegin();
+ const auto capacityBegin = begin() - offset;
+ const auto prependBufferEnd = begin();
+
+ if constexpr (!std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
+ // If construction can throw, and we have freeSpaceAtBegin(),
+ // it's easiest to just clear the container and start fresh.
+ // The alternative would be to keep track of two active, disjoint ranges.
+ if (offset) {
+ (*this)->truncate(0);
+ setBegin(capacityBegin);
+ offset = 0;
+ }
+ }
+
+ auto dst = capacityBegin;
+ const auto dend = end();
+ if (offset) { // avoids dead stores
+ setBegin(capacityBegin); // undo prepend optimization
+
+ // By construction, the following loop is nothrow!
+ // (otherwise, we can't reach here)
+ // Assumes InputIterator operations don't throw.
+ // (but we can't statically assert that, as these operations
+ // have preconditons, so typically aren't noexcept)
+ while (true) {
+ if (dst == prependBufferEnd) { // ran out of prepend buffer space
+ size += offset;
+ // we now have a contiguous buffer, continue with the main loop:
+ break;
+ }
+ if (first == last) { // ran out of elements to assign
+ std::destroy(prependBufferEnd, dend);
+ size = dst - begin();
+ return;
+ }
+ // construct element in prepend buffer
+ q20::construct_at(dst, std::invoke(proj, *first));
+ ++dst;
+ ++first;
+ }
+ }
+
+ while (true) {
+ if (first == last) { // ran out of elements to assign
+ std::destroy(dst, dend);
+ break;
+ }
+ if (dst == dend) { // ran out of existing elements to overwrite
+ if constexpr (IsFwdIt && IsIdentity) {
+ dst = std::uninitialized_copy(first, last, dst);
+ break;
+ } else if constexpr (IsFwdIt && !IsIdentity
+ && std::is_nothrow_constructible_v<T, decltype(std::invoke(proj, *first))>) {
+ for (; first != last; ++dst, ++first) // uninitialized_copy with projection
+ q20::construct_at(dst, std::invoke(proj, *first));
+ break;
+ } else {
+ do {
+ (*this)->emplace(size, std::invoke(proj, *first));
+ } while (++first != last);
+ return; // size() is already correct (and dst invalidated)!
+ }
+ }
+ *dst = std::invoke(proj, *first); // overwrite existing element
+ ++dst;
+ ++first;
+ }
+ 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.h b/src/corelib/tools/qatomicscopedvaluerollback.h
new file mode 100644
index 0000000000..8f653acba5
--- /dev/null
+++ b/src/corelib/tools/qatomicscopedvaluerollback.h
@@ -0,0 +1,127 @@
+// 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_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 QAtomicScopedValueRollback
+{
+ std::atomic<T> &m_atomic;
+ T m_value;
+ std::memory_order m_mo;
+
+ Q_DISABLE_COPY_MOVE(QAtomicScopedValueRollback)
+
+ static constexpr std::memory_order store_part(std::memory_order mo) noexcept
+ {
+ switch (mo) {
+ case std::memory_order_relaxed:
+ case std::memory_order_consume:
+ case std::memory_order_acquire: return std::memory_order_relaxed;
+ case std::memory_order_release:
+ 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 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();
+#endif
+ return std::memory_order_seq_cst;
+ }
+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(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)
+ : m_atomic(var), m_value(var.exchange(value, mo)), m_mo(mo) {}
+
+ //
+ // 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)
+ : QAtomicScopedValueRollback(var._q_value, value, mo) {}
+
+ //
+ // 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) {}
+
+ ~QAtomicScopedValueRollback()
+ {
+ m_atomic.store(m_value, store_part(m_mo));
+ }
+
+ void commit()
+ {
+ 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_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 ecd12d095c..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,7 +467,8 @@ quint32 QBitArray::toUInt32(QSysInfo::Endian endianness, bool *ok) const noexcep
\overload
*/
-/*! \fn QBitArray::QBitArray(const QBitArray &other)
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+/*! \fn QBitArray::QBitArray(const QBitArray &other) noexcept
Constructs a copy of \a other.
@@ -465,7 +480,7 @@ quint32 QBitArray::toUInt32(QSysInfo::Endian endianness, bool *ok) const noexcep
\sa operator=()
*/
-/*! \fn QBitArray &QBitArray::operator=(const QBitArray &other)
+/*! \fn QBitArray &QBitArray::operator=(const QBitArray &other) noexcept
Assigns \a other to this bit array and returns a reference to
this bit array.
@@ -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 8e19725339..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);
- QBitArray(const QBitArray &other) : d(other.d) {}
- inline QBitArray &operator=(const QBitArray &other) { d = other.d; return *this; }
+ // 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 e771877265..2880eedf77 100644
--- a/src/corelib/tools/qcommandlineparser.cpp
+++ b/src/corelib/tools/qcommandlineparser.cpp
@@ -22,7 +22,7 @@ using namespace Qt::StringLiterals;
extern void Q_CORE_EXPORT qt_call_post_routines();
-typedef QHash<QString, int> NameHash_t;
+typedef QHash<QString, qsizetype> NameHash_t;
class QCommandLineParserPrivate
{
@@ -55,7 +55,7 @@ public:
NameHash_t nameHash;
//! Option values found (only for options with a value)
- QHash<int, QStringList> optionValuesHash;
+ QHash<qsizetype, QStringList> optionValuesHash;
//! Names of options found on the command line.
QStringList optionNames;
@@ -125,24 +125,24 @@ QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
The parser handles short names, long names, more than one name for the same
option, and option values.
- Options on the command line are recognized as starting with a single or
- double \c{-} character(s).
+ Options on the command line are recognized as starting with one or two
+ \c{-} characters, followed by the option name.
The option \c{-} (single dash alone) is a special case, often meaning standard
- input, and not treated as an option. The parser will treat everything after the
+ input, and is not treated as an option. The parser will treat everything after the
option \c{--} (double dash) as positional arguments.
Short options are single letters. The option \c{v} would be specified by
passing \c{-v} on the command line. In the default parsing mode, short options
can be written in a compact form, for instance \c{-abc} is equivalent to \c{-a -b -c}.
- The parsing mode for can be set to ParseAsLongOptions, in which case \c{-abc}
+ The parsing mode can be changed to ParseAsLongOptions, in which case \c{-abc}
will be parsed as the long option \c{abc}.
Long options are more than one letter long and cannot be compacted together.
The long option \c{verbose} would be passed as \c{--verbose} or \c{-verbose}.
- Passing values to options can be done using the assignment operator: \c{-v=value}
- \c{--verbose=value}, or a space: \c{-v value} \c{--verbose value}, i.e. the next
- argument is used as value (even if it starts with a \c{-}).
+ Passing values to options can be done by using the assignment operator (\c{-v=value},
+ \c{--verbose=value}), or with a space (\c{-v value}, \c{--verbose value}). This
+ works even if the the value starts with a \c{-}.
The parser does not support optional values - if an option is set to
require a value, one must be present. If such an option is placed last
@@ -157,13 +157,13 @@ QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
Example:
\snippet code/src_corelib_tools_qcommandlineparser_main.cpp 0
- If your compiler supports the C++11 standard, the three addOption() calls in
- the above example can be simplified:
+ The three addOption() calls in the above example can be made more compact
+ by using addOptions():
\snippet code/src_corelib_tools_qcommandlineparser_main.cpp cxx11
Known limitation: the parsing of Qt options inside QCoreApplication and subclasses
happens before QCommandLineParser exists, so it can't take it into account. This
- means any option value that looks like a builtin Qt option, will be treated by
+ means any option value that looks like a builtin Qt option will be treated by
QCoreApplication as a builtin Qt option. Example: \c{--profile -reverse} will
lead to QGuiApplication seeing the -reverse option set, and removing it from
QCoreApplication::arguments() before QCommandLineParser defines the \c{profile}
@@ -176,7 +176,7 @@ QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
It is then advisable to introduce a function to do the command line parsing
which takes a struct or class receiving the option values returning an
- enumeration representing the result. The dnslookup example of the QtNetwork
+ object representing the result. The dnslookup example of the QtNetwork
module illustrates this:
\snippet dnslookup.h 0
@@ -204,20 +204,22 @@ QStringList QCommandLineParserPrivate::aliases(const QString &optionName) const
\code
- switch (parseCommandLine(parser, &query, &errorMessage)) {
- case CommandLineOk:
+ switch (parseResult.statusCode) {
+ case Status::Ok:
break;
- case CommandLineError:
+ case Status::Error: {
+ QString errorMessage = parseResult.errorString.value_or(u"Unknown error occurred"_qs);
QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
"<html><head/><body><h2>" + errorMessage + "</h2><pre>"
+ parser.helpText() + "</pre></body></html>");
return 1;
- case CommandLineVersionRequested:
+ }
+ case Status::VersionRequested:
QMessageBox::information(0, QGuiApplication::applicationDisplayName(),
QGuiApplication::applicationDisplayName() + ' '
+ QCoreApplication::applicationVersion());
return 0;
- case CommandLineHelpRequested:
+ case Status::HelpRequested:
QMessageBox::warning(0, QGuiApplication::applicationDisplayName(),
"<html><head/><body><pre>"
+ parser.helpText() + "</pre></body></html>");
@@ -251,7 +253,7 @@ QCommandLineParser::~QCommandLineParser()
\enum QCommandLineParser::SingleDashWordOptionMode
This enum describes the way the parser interprets command-line
- options that use a single dash followed by multiple letters, as as \c{-abc}.
+ options that use a single dash followed by multiple letters, as \c{-abc}.
\value ParseAsCompactedShortOptions \c{-abc} is interpreted as \c{-a -b -c},
i.e. as three short options that have been compacted on the command-line,
@@ -339,7 +341,7 @@ bool QCommandLineParser::addOption(const QCommandLineOption &option)
d->commandLineOptionList.append(option);
- const int offset = d->commandLineOptionList.size() - 1;
+ const qsizetype offset = d->commandLineOptionList.size() - 1;
for (const QString &name : optionNames)
d->nameHash.insert(name, offset);
@@ -387,13 +389,17 @@ QCommandLineOption QCommandLineParser::addVersionOption()
}
/*!
- Adds the help option (\c{-h}, \c{--help} and \c{-?} on Windows)
- as well as an option \c{--help-all} to include Qt-specific options in the output.
+ Adds help options to the command-line parser.
+
+ The options specified for this command-line are described by \c{-h} or
+ \c{--help}. On Windows, the alternative \c{-?} is also supported. The option
+ \c{--help-all} extends that to include generic Qt options, not defined by
+ this command, in the output.
These options are handled automatically by QCommandLineParser.
- Remember to use setApplicationDescription to set the application description,
- which will be displayed when this option is used.
+ Remember to use setApplicationDescription() to set the application
+ description, which will be displayed when this option is used.
Example:
\snippet code/src_corelib_tools_qcommandlineparser_main.cpp 0
@@ -409,7 +415,8 @@ QCommandLineOption QCommandLineParser::addHelpOption()
<< QStringLiteral("h")
<< QStringLiteral("help"), tr("Displays help on commandline options."));
addOption(opt);
- QCommandLineOption optHelpAll(QStringLiteral("help-all"), tr("Displays help including Qt specific options."));
+ QCommandLineOption optHelpAll(QStringLiteral("help-all"),
+ tr("Displays help, including generic Qt options."));
addOption(optHelpAll);
d->builtinHelpOption = true;
return opt;
@@ -500,9 +507,9 @@ QString QCommandLineParser::errorText() const
{
if (!d->errorText.isEmpty())
return d->errorText;
- if (d->unknownOptionNames.count() == 1)
- return tr("Unknown option '%1'.").arg(d->unknownOptionNames.first());
- if (d->unknownOptionNames.count() > 1)
+ if (d->unknownOptionNames.size() == 1)
+ 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();
}
@@ -514,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);
@@ -626,7 +634,7 @@ bool QCommandLineParserPrivate::parseOptionValue(const QString &optionName, cons
const QLatin1Char assignChar('=');
const NameHash_t::const_iterator nameHashIt = nameHash.constFind(optionName);
if (nameHashIt != nameHash.constEnd()) {
- const int assignPos = argument.indexOf(assignChar);
+ const qsizetype assignPos = argument.indexOf(assignChar);
const NameHash_t::mapped_type optionOffset = *nameHashIt;
const bool withValue = !commandLineOptionList.at(optionOffset).valueName().isEmpty();
if (withValue) {
@@ -666,7 +674,6 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
needsParsing = false;
bool error = false;
- const QString doubleDashString(QStringLiteral("--"));
const QLatin1Char dashChar('-');
const QLatin1Char assignChar('=');
@@ -690,8 +697,8 @@ bool QCommandLineParserPrivate::parse(const QStringList &args)
if (forcePositional) {
positionalArgumentList.append(argument);
- } else if (argument.startsWith(doubleDashString)) {
- if (argument.length() > 2) {
+ } else if (argument.startsWith("--"_L1)) {
+ if (argument.size() > 2) {
QString optionName = argument.mid(2).section(assignChar, 0, 0);
if (registerFoundOption(optionName)) {
if (!parseOptionValue(optionName, argument, &argumentIterator, args.end()))
@@ -779,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.
@@ -793,7 +800,7 @@ bool QCommandLineParser::isSet(const QString &name) const
if (d->optionNames.contains(name))
return true;
const QStringList aliases = d->aliases(name);
- for (const QString &optionName : qAsConst(d->optionNames)) {
+ for (const QString &optionName : std::as_const(d->optionNames)) {
if (aliases.contains(optionName))
return true;
}
@@ -805,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.
@@ -813,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()
*/
@@ -834,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.
@@ -850,12 +857,18 @@ QString QCommandLineParser::value(const QString &optionName) const
QStringList QCommandLineParser::values(const QString &optionName) const
{
d->checkParsed("values");
- const NameHash_t::const_iterator it = d->nameHash.constFind(optionName);
+ auto it = d->nameHash.constFind(optionName);
if (it != d->nameHash.cend()) {
- const int optionOffset = *it;
+ 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;
}
@@ -942,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
@@ -1038,20 +1051,20 @@ static QString wrapText(const QString &names, int optionNameMaxWidth, const QStr
};
QString text;
- int lineStart = 0;
- int lastBreakable = -1;
+ qsizetype lineStart = 0;
+ qsizetype lastBreakable = -1;
const int max = 79 - (indentation.size() + optionNameMaxWidth + 1);
int x = 0;
- const int len = description.length();
+ const qsizetype len = description.size();
- for (int i = 0; i < len; ++i) {
+ for (qsizetype i = 0; i < len; ++i) {
++x;
const QChar c = description.at(i);
if (c.isSpace())
lastBreakable = i;
- int breakAt = -1;
- int nextLineStart = -1;
+ qsizetype breakAt = -1;
+ qsizetype nextLineStart = -1;
if (x > max && lastBreakable != -1) {
// time to break and we know where
breakAt = lastBreakable;
@@ -1067,7 +1080,7 @@ static QString wrapText(const QString &names, int optionNameMaxWidth, const QStr
}
if (breakAt != -1) {
- const int numChars = breakAt - lineStart;
+ const qsizetype numChars = breakAt - lineStart;
//qDebug() << "breakAt=" << description.at(breakAt) << "breakAtSpace=" << breakAtSpace << lineStart << "to" << breakAt << description.mid(lineStart, numChars);
text += indentation + nextNameSection().leftJustified(optionNameMaxWidth) + u' ';
text += QStringView{description}.mid(lineStart, numChars) + nl;
@@ -1093,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);
@@ -1109,14 +1123,14 @@ QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const
text += QCommandLineParser::tr("Options:") + nl;
QStringList optionNameList;
optionNameList.reserve(options.size());
- int longestOptionNameString = 0;
- for (const QCommandLineOption &option : qAsConst(options)) {
+ qsizetype longestOptionNameString = 0;
+ for (const QCommandLineOption &option : std::as_const(options)) {
if (option.flags() & QCommandLineOption::HiddenFromHelp)
continue;
const QStringList optionNames = option.names();
QString optionNamesString;
for (const QString &optionName : optionNames) {
- const int numDashes = optionName.length() == 1 ? 1 : 2;
+ const int numDashes = optionName.size() == 1 ? 1 : 2;
optionNamesString += QLatin1StringView("--", numDashes) + optionName + ", "_L1;
}
if (!optionNames.isEmpty())
@@ -1125,12 +1139,12 @@ QString QCommandLineParserPrivate::helpText(bool includeQtOptions) const
if (!valueName.isEmpty())
optionNamesString += " <"_L1 + valueName + u'>';
optionNameList.append(optionNamesString);
- longestOptionNameString = qMax(longestOptionNameString, optionNamesString.length());
+ longestOptionNameString = qMax(longestOptionNameString, optionNamesString.size());
}
++longestOptionNameString;
- const int optionNameMaxWidth = qMin(50, longestOptionNameString);
+ const int optionNameMaxWidth = qMin(50, int(longestOptionNameString));
auto optionNameIterator = optionNameList.cbegin();
- for (const QCommandLineOption &option : qAsConst(options)) {
+ for (const QCommandLineOption &option : std::as_const(options)) {
if (option.flags() & QCommandLineOption::HiddenFromHelp)
continue;
text += wrapText(*optionNameIterator, optionNameMaxWidth, option.description());
diff --git a/src/corelib/tools/qcontainerfwd.h b/src/corelib/tools/qcontainerfwd.h
index c5e1ba2b62..d5590553fa 100644
--- a/src/corelib/tools/qcontainerfwd.h
+++ b/src/corelib/tools/qcontainerfwd.h
@@ -1,13 +1,20 @@
// 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
-#include <QtCore/qglobal.h>
-
#ifndef QCONTAINERFWD_H
#define QCONTAINERFWD_H
+#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qtypes.h>
+
+#if 0
+#pragma qt_class(QtContainerFwd)
+#endif
+
// std headers can unfortunately not be forward declared
+#include <cstddef> // std::size_t
#include <utility>
+#include <limits>
QT_BEGIN_NAMESPACE
@@ -16,16 +23,22 @@ 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;
-#ifndef Q_CLANG_QDOC
+class QString;
+#ifndef Q_QDOC
template<typename T> using QVector = QList<T>;
using QStringList = QList<QString>;
+class QByteArray;
using QByteArrayList = QList<QByteArray>;
#else
template<typename T> class QVector;
@@ -38,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 4a5f9f184f..998bc292d4 100644
--- a/src/corelib/tools/qcontainertools_impl.h
+++ b/src/corelib/tools/qcontainertools_impl.h
@@ -14,6 +14,8 @@
#include <QtCore/qglobal.h>
#include <QtCore/qtypeinfo.h>
+#include <QtCore/qxptype_traits.h>
+
#include <cstring>
#include <iterator>
#include <memory>
@@ -37,6 +39,26 @@ static constexpr bool q_points_into_range(const T *p, const T *b, const T *e,
return !less(p, b) && less(p, e);
}
+/*!
+ \internal
+
+ Returns whether \a p is within container \a c. In its simplest form equivalent to:
+ c.data() <= p < c.data() + c.size()
+*/
+template <typename C, typename T>
+static constexpr bool q_points_into_range(const T &p, const C &c) noexcept
+{
+ static_assert(std::is_same_v<decltype(std::data(c)), T>);
+
+ // std::distance because QArrayDataPointer has a "qsizetype size"
+ // member but no size() function
+ return q_points_into_range(p, std::data(c),
+ std::data(c) + std::distance(std::begin(c), std::end(c)));
+}
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
+
template <typename T, typename N>
void q_uninitialized_move_if_noexcept_n(T* first, N n, T* out)
{
@@ -50,10 +72,12 @@ template <typename T, typename N>
void q_uninitialized_relocate_n(T* first, N n, T* out)
{
if constexpr (QTypeInfo<T>::isRelocatable) {
- if (n != N(0)) { // even if N == 0, out == nullptr or first == nullptr are UB for memmove()
- std::memmove(static_cast<void*>(out),
- static_cast<const void*>(first),
- n * sizeof(T));
+ 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),
+ n * sizeof(T));
}
} else {
q_uninitialized_move_if_noexcept_n(first, n, out);
@@ -62,6 +86,63 @@ void q_uninitialized_relocate_n(T* first, N n, T* out)
}
}
+QT_WARNING_POP
+
+/*!
+ \internal
+
+ A wrapper around std::rotate(), with an optimization for
+ Q_RELOCATABLE_TYPEs. We omit the return value, as it would be more work to
+ compute in the Q_RELOCATABLE_TYPE case and, unlike std::rotate on
+ ForwardIterators, callers can compute the result in constant time
+ themselves.
+*/
+template <typename T>
+void q_rotate(T *first, T *mid, T *last)
+{
+ if constexpr (QTypeInfo<T>::isRelocatable) {
+ const auto cast = [](T *p) { return reinterpret_cast<uchar*>(p); };
+ std::rotate(cast(first), cast(mid), cast(last));
+ } else {
+ std::rotate(first, mid, last);
+ }
+}
+
+/*!
+ \internal
+ Copies all elements, except the ones for which \a pred returns \c true, from
+ range [first, last), to the uninitialized memory buffer starting at \a out.
+
+ It's undefined behavior if \a out points into [first, last).
+
+ Returns a pointer one past the last copied element.
+
+ If an exception is thrown, all the already copied elements in the destination
+ buffer are destroyed.
+*/
+template <typename T, typename Predicate>
+T *q_uninitialized_remove_copy_if(T *first, T *last, T *out, Predicate &pred)
+{
+ static_assert(std::is_nothrow_destructible_v<T>,
+ "This algorithm requires that T has a non-throwing destructor");
+ Q_ASSERT(!q_points_into_range(out, first, last));
+
+ T *dest_begin = out;
+ QT_TRY {
+ while (first != last) {
+ if (!pred(*first)) {
+ new (std::addressof(*out)) T(*first);
+ ++out;
+ }
+ ++first;
+ }
+ } QT_CATCH (...) {
+ std::destroy(std::reverse_iterator(out), std::reverse_iterator(dest_begin));
+ QT_RETHROW;
+ }
+ return out;
+}
+
template<typename iterator, typename N>
void q_relocate_overlap_n_left_move(iterator first, N n, iterator d_first)
{
@@ -173,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,
@@ -203,43 +291,38 @@ void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l
c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
}
-template <typename Iterator, typename = std::void_t<>>
-struct AssociativeIteratorHasKeyAndValue : std::false_type
-{
-};
-
template <typename Iterator>
-struct AssociativeIteratorHasKeyAndValue<
- Iterator,
- std::void_t<decltype(std::declval<Iterator &>().key()),
- decltype(std::declval<Iterator &>().value())>
- >
- : std::true_type
-{
-};
-
-template <typename Iterator, typename = std::void_t<>, typename = std::void_t<>>
-struct AssociativeIteratorHasFirstAndSecond : std::false_type
-{
-};
+using KeyAndValueTest = decltype(
+ std::declval<Iterator &>().key(),
+ std::declval<Iterator &>().value()
+);
template <typename Iterator>
-struct AssociativeIteratorHasFirstAndSecond<
- Iterator,
- std::void_t<decltype(std::declval<Iterator &>()->first),
- decltype(std::declval<Iterator &>()->second)>
- >
- : std::true_type
-{
-};
+using FirstAndSecondTest = decltype(
+ std::declval<Iterator &>()->first,
+ std::declval<Iterator &>()->second
+);
template <typename Iterator>
using IfAssociativeIteratorHasKeyAndValue =
- typename std::enable_if<AssociativeIteratorHasKeyAndValue<Iterator>::value, bool>::type;
+ std::enable_if_t<qxp::is_detected_v<KeyAndValueTest, Iterator>, bool>;
template <typename Iterator>
using IfAssociativeIteratorHasFirstAndSecond =
- typename std::enable_if<AssociativeIteratorHasFirstAndSecond<Iterator>::value, bool>::type;
+ 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(
+ std::declval<Iterator &>().operator--()
+);
+
+template <typename Iterator>
+using IfIteratorCanMoveBackwards =
+ std::enable_if_t<qxp::is_detected_v<MoveBackwardsTest, Iterator>, bool>;
template <typename T, typename U>
using IfIsNotSame =
@@ -297,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.cpp b/src/corelib/tools/qcontiguouscache.cpp
index 73cfed97f2..d28d1e7153 100644
--- a/src/corelib/tools/qcontiguouscache.cpp
+++ b/src/corelib/tools/qcontiguouscache.cpp
@@ -6,6 +6,8 @@
#include <QDebug>
#endif
+#include <QtCore/qmalloc.h>
+
QT_BEGIN_NAMESPACE
#ifdef QT_QCONTIGUOUSCACHE_DEBUG
diff --git a/src/corelib/tools/qcontiguouscache.h b/src/corelib/tools/qcontiguouscache.h
index 70703fe0ab..c01dbb9390 100644
--- a/src/corelib/tools/qcontiguouscache.h
+++ b/src/corelib/tools/qcontiguouscache.h
@@ -5,7 +5,13 @@
#define QCONTIGUOUSCACHE_H
#include <QtCore/qatomic.h>
-#include <limits.h>
+#include <QtCore/qassert.h>
+#include <QtCore/qtclasshelpermacros.h>
+#include <QtCore/qtcoreexports.h>
+#include <QtCore/qtypeinfo.h>
+
+#include <climits>
+#include <limits>
#include <new>
QT_BEGIN_NAMESPACE
@@ -63,7 +69,7 @@ public:
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QContiguousCache)
void swap(QContiguousCache &other) noexcept { qt_ptr_swap(d, other.d); }
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename U = T>
QTypeTraits::compare_eq_result<U> operator==(const QContiguousCache<T> &other) const
{
@@ -85,7 +91,7 @@ public:
#else
bool operator==(const QContiguousCache &other) const;
bool operator!=(const QContiguousCache &other) const;
-#endif // Q_CLANG_QDOC
+#endif // Q_QDOC
inline qsizetype capacity() const {return d->alloc; }
inline qsizetype count() const { return d->count; }
diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp
index 8d60be175e..2e82a394ee 100644
--- a/src/corelib/tools/qcryptographichash.cpp
+++ b/src/corelib/tools/qcryptographichash.cpp
@@ -1,9 +1,19 @@
-// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2013 Ruslan Nigmatullin <euroelessar@yandex.ru>
// Copyright (C) 2013 Richard J. Moore <rich@kde.org>.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qcryptographichash.h>
+#include <qmessageauthenticationcode.h>
+
#include <qiodevice.h>
+#include <qmutex.h>
+#include <qvarlengtharray.h>
+#include <private/qlocking_p.h>
+
+#include <array>
+#include <climits>
+#include <numeric>
#include "../../3rdparty/sha1/sha1.cpp"
@@ -11,12 +21,17 @@
# error "Are you sure you need the other hashing algorithms besides SHA-1?"
#endif
+// Header from rfc6234
+#include "../../3rdparty/rfc6234/sha.h"
+
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+#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;
@@ -57,9 +72,7 @@ Q_CONSTINIT static SHA3Final * const sha3Final = Final;
#endif
-// Header from rfc6234
-#include "../../3rdparty/rfc6234/sha.h"
-
+#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'
@@ -91,6 +104,9 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt
uint64_t addTemp;
return SHA384_512AddLengthM(context, length);
}
+#endif // !QT_CONFIG(opensslv30)
+
+#include "qtcore-config_p.h"
#if QT_CONFIG(system_libb2)
#include <blake2.h>
@@ -100,17 +116,88 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt
#endif
#endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(openssl_hash)
+#define USING_OPENSSL30
+#include <openssl/evp.h>
+#include <openssl/provider.h>
+#endif
+
QT_BEGIN_NAMESPACE
-static constexpr qsizetype MaxHashLength = 64;
+template <size_t N>
+class QSmallByteArray
+{
+ std::array<quint8, N> m_data;
+ static_assert(N <= std::numeric_limits<std::uint8_t>::max());
+ quint8 m_size = 0;
+public:
+ QSmallByteArray() = default;
+ // all compiler-generated SMFs are ok!
+ template <std::size_t M, std::enable_if_t<M < N, bool> = true> // M == N is for copy ctor!
+ constexpr QSmallByteArray(const QSmallByteArray<M> &other) noexcept
+ {
+ assign(other);
+ }
+ template <std::size_t M, std::enable_if_t<M < N, bool> = true> // M == N is for copy-assignment op!
+ constexpr QSmallByteArray &operator=(const QSmallByteArray<M> &other) noexcept
+ {
+ assign(other);
+ return *this;
+ }
+
+ template <typename Container> // ### underconstrained
+ constexpr void assign(const Container &c)
+ {
+ const size_t otherSize = size_t(std::size(c));
+ Q_ASSERT(otherSize < N);
+ memcpy(data(), std::data(c), otherSize);
+ m_size = quint8(otherSize);
+ }
+
+ constexpr quint8 *data() noexcept { return m_data.data(); }
+ constexpr const quint8 *data() const noexcept { return m_data.data(); }
+ constexpr qsizetype size() const noexcept { return qsizetype{m_size}; }
+ constexpr quint8 &operator[](qsizetype n)
+ {
+ Q_ASSERT(n < size());
+ return data()[n];
+ }
+ constexpr const quint8 &operator[](qsizetype n) const
+ {
+ Q_ASSERT(n < size());
+ return data()[n];
+ }
+ constexpr bool isEmpty() const noexcept { return size() == 0; }
+ constexpr void clear() noexcept { m_size = 0; }
+ constexpr void resizeForOverwrite(qsizetype s)
+ {
+ Q_ASSERT(s >= 0);
+ Q_ASSERT(size_t(s) <= N);
+ m_size = std::uint8_t(s);
+ }
+ constexpr void resize(qsizetype s, quint8 v)
+ {
+ const auto oldSize = size();
+ resizeForOverwrite(s);
+ if (s > oldSize)
+ memset(data() + oldSize, v, size() - oldSize);
+ }
+ constexpr QByteArrayView toByteArrayView() const noexcept
+ { return *this; }
+
+ constexpr auto begin() noexcept { return data(); }
+ constexpr auto begin() const noexcept { return data(); }
+ constexpr auto cbegin() const noexcept { return begin(); }
+ constexpr auto end() noexcept { return data() + size(); }
+ constexpr auto end() const noexcept { return data() + size(); }
+ constexpr auto cend() const noexcept { return end(); }
+};
static constexpr int hashLengthInternal(QCryptographicHash::Algorithm method) noexcept
{
switch (method) {
#define CASE(Enum, Size) \
case QCryptographicHash:: Enum : \
- /* if this triggers, then increase MaxHashLength accordingly */ \
- static_assert(MaxHashLength >= qsizetype(Size) ); \
return Size \
/*end*/
CASE(Sha1, 20);
@@ -124,95 +211,184 @@ static constexpr int hashLengthInternal(QCryptographicHash::Algorithm method) no
CASE(Blake2s_128, 128 / 8);
case QCryptographicHash::Blake2b_160:
case QCryptographicHash::Blake2s_160:
- static_assert(160 / 8 <= MaxHashLength);
return 160 / 8;
case QCryptographicHash::RealSha3_224:
case QCryptographicHash::Keccak_224:
case QCryptographicHash::Blake2s_224:
- static_assert(224 / 8 <= MaxHashLength);
return 224 / 8;
case QCryptographicHash::RealSha3_256:
case QCryptographicHash::Keccak_256:
case QCryptographicHash::Blake2b_256:
case QCryptographicHash::Blake2s_256:
- static_assert(256 / 8 <= MaxHashLength);
return 256 / 8;
case QCryptographicHash::RealSha3_384:
case QCryptographicHash::Keccak_384:
case QCryptographicHash::Blake2b_384:
- static_assert(384 / 8 <= MaxHashLength);
return 384 / 8;
case QCryptographicHash::RealSha3_512:
case QCryptographicHash::Keccak_512:
case QCryptographicHash::Blake2b_512:
- static_assert(512 / 8 <= MaxHashLength);
return 512 / 8;
#endif
#undef CASE
+ case QCryptographicHash::NumAlgorithms: ;
+ // fall through
+ // Q_UNREACHABLE() would be BiC here, as hashLength(~~invalid~~) worked in 6.4
}
return 0;
}
+static constexpr int maxHashLength()
+{
+ int result = 0;
+ using A = QCryptographicHash::Algorithm;
+ for (int i = 0; i < A::NumAlgorithms; ++i)
+ result = std::max(result, hashLengthInternal(A(i)));
+ return result;
+}
+
+using HashResult = QSmallByteArray<maxHashLength()>;
+
+#ifdef USING_OPENSSL30
+static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept
+{
+ switch (method) {
+#define CASE(Enum, Name) \
+ case QCryptographicHash:: Enum : \
+ return Name \
+ /*end*/
+ CASE(Sha1, "SHA1");
+ CASE(Md4, "MD4");
+ CASE(Md5, "MD5");
+ CASE(Sha224, "SHA224");
+ CASE(Sha256, "SHA256");
+ CASE(Sha384, "SHA384");
+ CASE(Sha512, "SHA512");
+ CASE(RealSha3_224, "SHA3-224");
+ CASE(RealSha3_256, "SHA3-256");
+ CASE(RealSha3_384, "SHA3-384");
+ CASE(RealSha3_512, "SHA3-512");
+ CASE(Blake2b_512, "BLAKE2B512");
+ CASE(Blake2s_256, "BLAKE2S256");
+#undef CASE
+ default: return nullptr;
+ }
+}
+
+/*
+ Checks whether given method is not provided by OpenSSL and whether we will
+ have a fallback to non-OpenSSL implementation.
+*/
+static constexpr bool useNonOpenSSLFallback(QCryptographicHash::Algorithm method) noexcept
+{
+ 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;
+
+ return false;
+}
+#endif // USING_OPENSSL30
+
class QCryptographicHashPrivate
{
public:
explicit QCryptographicHashPrivate(QCryptographicHash::Algorithm method) noexcept
- : method(method)
+ : state(method), method(method)
+ {
+ }
+ ~QCryptographicHashPrivate()
{
- reset();
+ state.destroy(method);
}
void reset() noexcept;
void addData(QByteArrayView bytes) noexcept;
+ bool addData(QIODevice *dev);
void finalize() noexcept;
+ // when not called from the static hash() function, this function needs to be
+ // called with finalizeMutex held (finalize() will do that):
+ void finalizeUnchecked() noexcept;
+ // END functions that need to be called with finalizeMutex held
QByteArrayView resultView() const noexcept { return result.toByteArrayView(); }
+ static bool supportsAlgorithm(QCryptographicHash::Algorithm method);
+
+#ifdef USING_OPENSSL30
+ struct EVP_MD_CTX_deleter {
+ void operator()(EVP_MD_CTX *ctx) const noexcept {
+ EVP_MD_CTX_free(ctx);
+ }
+ };
+ struct EVP_MD_deleter {
+ void operator()(EVP_MD *md) const noexcept {
+ 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);
+ void reset() noexcept;
+ void finalizeUnchecked(HashResult &result) noexcept;
+ };
+#endif
+
+ union State {
+ explicit State(QCryptographicHash::Algorithm method);
+ void destroy(QCryptographicHash::Algorithm method);
+#ifdef USING_OPENSSL30
+ ~State() {}
+#endif
+
+ void reset(QCryptographicHash::Algorithm method) noexcept;
+ void addData(QCryptographicHash::Algorithm method, QByteArrayView data) noexcept;
+ void finalizeUnchecked(QCryptographicHash::Algorithm method, HashResult &result) noexcept;
- const QCryptographicHash::Algorithm method;
- union {
Sha1State sha1Context;
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+#ifdef USING_OPENSSL30
+ EVP evp;
+#else
MD5Context md5Context;
md4_context md4Context;
SHA224Context sha224Context;
SHA256Context sha256Context;
SHA384Context sha384Context;
SHA512Context sha512Context;
+#endif
SHA3Context sha3Context;
+
+ enum class Sha3Variant { Sha3, Keccak };
+ void sha3Finish(HashResult &result, int bitCount, Sha3Variant sha3Variant);
blake2b_state blake2bContext;
blake2s_state blake2sContext;
#endif
- };
-#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
- enum class Sha3Variant
- {
- Sha3,
- Keccak
- };
- void sha3Finish(int bitCount, Sha3Variant sha3Variant);
-#endif
- class SmallByteArray {
- std::array<char, MaxHashLength> m_data;
- static_assert(MaxHashLength <= std::numeric_limits<std::uint8_t>::max());
- std::uint8_t m_size;
- public:
- char *data() noexcept { return m_data.data(); }
- const char *data() const noexcept { return m_data.data(); }
- qsizetype size() const noexcept { return qsizetype{m_size}; }
- bool isEmpty() const noexcept { return size() == 0; }
- void clear() noexcept { m_size = 0; }
- void resizeForOverwrite(qsizetype s) {
- Q_ASSERT(s >= 0);
- Q_ASSERT(s <= MaxHashLength);
- m_size = std::uint8_t(s);
- }
- QByteArrayView toByteArrayView() const noexcept
- { return QByteArrayView{data(), size()}; }
- };
- SmallByteArray result;
+ } state;
+ // protects result in finalize()
+ QBasicMutex finalizeMutex;
+ HashResult result;
+
+ const QCryptographicHash::Algorithm method;
};
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
-void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant)
+void QCryptographicHashPrivate::State::sha3Finish(HashResult &result, int bitCount,
+ Sha3Variant sha3Variant)
{
/*
FIPS 202 §6.1 defines SHA-3 in terms of calculating the Keccak function
@@ -248,7 +424,7 @@ void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant
break;
}
- sha3Final(&copy, reinterpret_cast<BitSequence *>(result.data()));
+ sha3Final(&copy, result.data());
}
#endif
@@ -305,6 +481,7 @@ void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant
\omitvalue RealSha3_256
\omitvalue RealSha3_384
\omitvalue RealSha3_512
+ \omitvalue NumAlgorithms
*/
/*!
@@ -316,6 +493,18 @@ QCryptographicHash::QCryptographicHash(Algorithm method)
}
/*!
+ \fn QCryptographicHash::QCryptographicHash(QCryptographicHash &&other)
+
+ Move-constructs a new QCryptographicHash from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+
+ \since 6.5
+*/
+
+/*!
Destroys the object.
*/
QCryptographicHash::~QCryptographicHash()
@@ -324,6 +513,27 @@ QCryptographicHash::~QCryptographicHash()
}
/*!
+ \fn QCryptographicHash &QCryptographicHash::operator=(QCryptographicHash &&other)
+
+ Move-assigns \a other to this QCryptographicHash instance.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+
+ \since 6.5
+*/
+
+/*!
+ \fn void QCryptographicHash::swap(QCryptographicHash &other)
+
+ Swaps cryptographic hash \a other with this cryptographic hash. This
+ operation is very fast and never fails.
+
+ \since 6.5
+*/
+
+/*!
Resets the object.
*/
void QCryptographicHash::reset() noexcept
@@ -331,12 +541,101 @@ void QCryptographicHash::reset() noexcept
d->reset();
}
-void QCryptographicHashPrivate::reset() noexcept
+/*!
+ Returns the algorithm used to generate the cryptographic hash.
+
+ \since 6.5
+*/
+QCryptographicHash::Algorithm QCryptographicHash::algorithm() const noexcept
+{
+ return d->method;
+}
+
+#ifdef USING_OPENSSL30
+
+QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method)
+{
+ 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 ||
+ method == QCryptographicHash::Blake2s_160 ||
+ method == QCryptographicHash::Blake2s_224) {
+ new (&blake2sContext) blake2s_state;
+ reset(method);
+ } else {
+ new (&evp) EVP(method);
+ }
+}
+
+void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm method)
+{
+ 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) {
+ evp.~EVP();
+ }
+}
+
+QCryptographicHashPrivate::EVP::EVP(QCryptographicHash::Algorithm method)
+ : initializationFailed{true}
+{
+ if (method == QCryptographicHash::Md4) {
+ /*
+ * We need to load the legacy provider in order to have the MD4
+ * algorithm available.
+ */
+ 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) {
+ return;
+ }
+
+ /*
+ * Using the "-fips" option will disable the global "fips=yes" for
+ * this one lookup and the algorithm can be fetched from any provider
+ * that implements the algorithm (including the FIPS provider).
+ */
+ algorithm = EVP_MD_ptr(EVP_MD_fetch(nullptr, methodToName(method), "-fips"));
+ if (!algorithm) {
+ return;
+ }
+
+ initializationFailed = !EVP_DigestInit_ex(context.get(), algorithm.get(), nullptr);
+}
+
+#else // USING_OPENSSL30
+
+QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method)
{
switch (method) {
case QCryptographicHash::Sha1:
new (&sha1Context) Sha1State;
- sha1InitState(&sha1Context);
break;
#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
default:
@@ -346,27 +645,21 @@ void QCryptographicHashPrivate::reset() noexcept
#else
case QCryptographicHash::Md4:
new (&md4Context) md4_context;
- md4_init(&md4Context);
break;
case QCryptographicHash::Md5:
new (&md5Context) MD5Context;
- MD5Init(&md5Context);
break;
case QCryptographicHash::Sha224:
new (&sha224Context) SHA224Context;
- SHA224Reset(&sha224Context);
break;
case QCryptographicHash::Sha256:
new (&sha256Context) SHA256Context;
- SHA256Reset(&sha256Context);
break;
case QCryptographicHash::Sha384:
new (&sha384Context) SHA384Context;
- SHA384Reset(&sha384Context);
break;
case QCryptographicHash::Sha512:
new (&sha512Context) SHA512Context;
- SHA512Reset(&sha512Context);
break;
case QCryptographicHash::RealSha3_224:
case QCryptographicHash::Keccak_224:
@@ -377,27 +670,134 @@ void QCryptographicHashPrivate::reset() noexcept
case QCryptographicHash::RealSha3_512:
case QCryptographicHash::Keccak_512:
new (&sha3Context) SHA3Context;
- sha3Init(&sha3Context, hashLengthInternal(method) * 8);
break;
case QCryptographicHash::Blake2b_160:
case QCryptographicHash::Blake2b_256:
case QCryptographicHash::Blake2b_384:
case QCryptographicHash::Blake2b_512:
new (&blake2bContext) blake2b_state;
- blake2b_init(&blake2bContext, hashLengthInternal(method));
break;
case QCryptographicHash::Blake2s_128:
case QCryptographicHash::Blake2s_160:
case QCryptographicHash::Blake2s_224:
case QCryptographicHash::Blake2s_256:
new (&blake2sContext) blake2s_state;
- blake2s_init(&blake2sContext, hashLengthInternal(method));
break;
#endif
+ case QCryptographicHash::NumAlgorithms:
+ Q_UNREACHABLE();
}
+ reset(method);
+}
+
+void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm)
+{
+ static_assert(std::is_trivially_destructible_v<State>); // so nothing to do here
+}
+#endif // !USING_OPENSSL30
+
+void QCryptographicHashPrivate::reset() noexcept
+{
result.clear();
+ state.reset(method);
+}
+
+#ifdef USING_OPENSSL30
+
+void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept
+{
+ 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 ||
+ method == QCryptographicHash::Blake2s_224) {
+ blake2s_init(&blake2sContext, hashLengthInternal(method));
+ } else {
+ evp.reset();
+ }
+}
+
+void QCryptographicHashPrivate::EVP::reset() noexcept
+{
+ if (!initializationFailed) {
+ Q_ASSERT(context);
+ Q_ASSERT(algorithm);
+ // everything already set up - just reset the context
+ EVP_MD_CTX_reset(context.get());
+ initializationFailed = !EVP_DigestInit_ex(context.get(), algorithm.get(), nullptr);
+ }
+ // if initializationFailed first time around, it will not succeed this time, either
+}
+
+#else // USING_OPENSSL30
+
+void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept
+{
+ switch (method) {
+ case QCryptographicHash::Sha1:
+ sha1InitState(&sha1Context);
+ break;
+#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ default:
+ Q_ASSERT_X(false, "QCryptographicHash", "Method not compiled in");
+ Q_UNREACHABLE();
+ break;
+#else
+ case QCryptographicHash::Md4:
+ md4_init(&md4Context);
+ break;
+ case QCryptographicHash::Md5:
+ MD5Init(&md5Context);
+ break;
+ case QCryptographicHash::Sha224:
+ SHA224Reset(&sha224Context);
+ break;
+ case QCryptographicHash::Sha256:
+ SHA256Reset(&sha256Context);
+ break;
+ case QCryptographicHash::Sha384:
+ SHA384Reset(&sha384Context);
+ break;
+ case QCryptographicHash::Sha512:
+ SHA512Reset(&sha512Context);
+ break;
+ case QCryptographicHash::RealSha3_224:
+ case QCryptographicHash::Keccak_224:
+ case QCryptographicHash::RealSha3_256:
+ case QCryptographicHash::Keccak_256:
+ case QCryptographicHash::RealSha3_384:
+ case QCryptographicHash::Keccak_384:
+ case QCryptographicHash::RealSha3_512:
+ case QCryptographicHash::Keccak_512:
+ sha3Init(&sha3Context, hashLengthInternal(method) * 8);
+ break;
+ case QCryptographicHash::Blake2b_160:
+ case QCryptographicHash::Blake2b_256:
+ case QCryptographicHash::Blake2b_384:
+ case QCryptographicHash::Blake2b_512:
+ blake2b_init(&blake2bContext, hashLengthInternal(method));
+ break;
+ case QCryptographicHash::Blake2s_128:
+ case QCryptographicHash::Blake2s_160:
+ case QCryptographicHash::Blake2s_224:
+ case QCryptographicHash::Blake2s_256:
+ blake2s_init(&blake2sContext, hashLengthInternal(method));
+ break;
+#endif
+ case QCryptographicHash::NumAlgorithms:
+ Q_UNREACHABLE();
+ }
}
+#endif // USING_OPENSSL30
+
#if QT_DEPRECATED_SINCE(6, 4)
/*!
Adds the first \a length chars of \a data to the cryptographic
@@ -426,6 +826,43 @@ void QCryptographicHash::addData(QByteArrayView bytes) noexcept
void QCryptographicHashPrivate::addData(QByteArrayView bytes) noexcept
{
+ state.addData(method, bytes);
+ result.clear();
+}
+
+#ifdef USING_OPENSSL30
+
+void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm method,
+ QByteArrayView bytes) noexcept
+{
+ const char *data = bytes.data();
+ auto length = bytes.size();
+ // all functions take size_t length, so we don't need to loop around them:
+ {
+ 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 ||
+ method == QCryptographicHash::Blake2s_224) {
+ blake2s_update(&blake2sContext, reinterpret_cast<const uint8_t *>(data), length);
+ } else if (!evp.initializationFailed) {
+ EVP_DigestUpdate(evp.context.get(), (const unsigned char *)data, length);
+ }
+ }
+}
+
+#else // USING_OPENSSL30
+
+void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm method,
+ QByteArrayView bytes) noexcept
+{
const char *data = bytes.data();
auto length = bytes.size();
@@ -489,10 +926,12 @@ void QCryptographicHashPrivate::addData(QByteArrayView bytes) noexcept
blake2s_update(&blake2sContext, reinterpret_cast<const uint8_t *>(data), length);
break;
#endif
+ case QCryptographicHash::NumAlgorithms:
+ Q_UNREACHABLE();
}
}
- result.clear();
}
+#endif // !USING_OPENSSL30
/*!
Reads the data from the open QIODevice \a device until it ends
@@ -501,6 +940,11 @@ void QCryptographicHashPrivate::addData(QByteArrayView bytes) noexcept
*/
bool QCryptographicHash::addData(QIODevice *device)
{
+ return d->addData(device);
+}
+
+bool QCryptographicHashPrivate::addData(QIODevice *device)
+{
if (!device->isReadable())
return false;
@@ -508,10 +952,10 @@ bool QCryptographicHash::addData(QIODevice *device)
return false;
char buffer[1024];
- int length;
+ qint64 length;
while ((length = device->read(buffer, sizeof(buffer))) > 0)
- d->addData({buffer, length});
+ addData({buffer, qsizetype(length)}); // length always <= 1024
return device->atEnd();
}
@@ -539,21 +983,86 @@ QByteArray QCryptographicHash::result() const
*/
QByteArrayView QCryptographicHash::resultView() const noexcept
{
+ // resultView() is a const function, so concurrent calls are allowed; protect:
d->finalize();
+ // resultView() remains(!) valid even after we dropped the mutex in finalize()
return d->resultView();
}
+/*!
+ \internal
+
+ Calls finalizeUnchecked(), if needed, under finalizeMutex protection.
+*/
void QCryptographicHashPrivate::finalize() noexcept
{
+ const auto lock = qt_scoped_lock(finalizeMutex);
+ // check that no other thread already finalizeUnchecked()'ed before us:
if (!result.isEmpty())
return;
+ finalizeUnchecked();
+}
+/*!
+ \internal
+
+ Must be called with finalizeMutex held (except from static hash() function,
+ where no sharing can take place).
+*/
+void QCryptographicHashPrivate::finalizeUnchecked() noexcept
+{
+ state.finalizeUnchecked(method, result);
+}
+
+#ifdef USING_OPENSSL30
+void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method,
+ HashResult &result) noexcept
+{
+ 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);
+ blake2b_final(&copy, result.data(), length);
+ } else if (method == QCryptographicHash::Blake2s_128 ||
+ method == QCryptographicHash::Blake2s_160 ||
+ method == QCryptographicHash::Blake2s_224) {
+ const auto length = hashLengthInternal(method);
+ blake2s_state copy = blake2sContext;
+ result.resizeForOverwrite(length);
+ blake2s_final(&copy, result.data(), length);
+ } else {
+ evp.finalizeUnchecked(result);
+ }
+}
+
+void QCryptographicHashPrivate::EVP::finalizeUnchecked(HashResult &result) noexcept
+{
+ if (!initializationFailed) {
+ EVP_MD_CTX_ptr copy = EVP_MD_CTX_ptr(EVP_MD_CTX_new());
+ EVP_MD_CTX_copy_ex(copy.get(), context.get());
+ result.resizeForOverwrite(EVP_MD_get_size(algorithm.get()));
+ EVP_DigestFinal_ex(copy.get(), result.data(), nullptr);
+ }
+}
+
+#else // USING_OPENSSL30
+
+void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method,
+ HashResult &result) noexcept
+{
switch (method) {
case QCryptographicHash::Sha1: {
Sha1State copy = sha1Context;
result.resizeForOverwrite(20);
sha1FinalizeState(&copy);
- sha1ToHash(&copy, (unsigned char *)result.data());
+ sha1ToHash(&copy, result.data());
break;
}
#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
@@ -565,51 +1074,51 @@ void QCryptographicHashPrivate::finalize() noexcept
case QCryptographicHash::Md4: {
md4_context copy = md4Context;
result.resizeForOverwrite(MD4_RESULTLEN);
- md4_final(&copy, (unsigned char *)result.data());
+ md4_final(&copy, result.data());
break;
}
case QCryptographicHash::Md5: {
MD5Context copy = md5Context;
result.resizeForOverwrite(16);
- MD5Final(&copy, (unsigned char *)result.data());
+ MD5Final(&copy, result.data());
break;
}
case QCryptographicHash::Sha224: {
SHA224Context copy = sha224Context;
result.resizeForOverwrite(SHA224HashSize);
- SHA224Result(&copy, reinterpret_cast<unsigned char *>(result.data()));
+ SHA224Result(&copy, result.data());
break;
}
case QCryptographicHash::Sha256: {
SHA256Context copy = sha256Context;
result.resizeForOverwrite(SHA256HashSize);
- SHA256Result(&copy, reinterpret_cast<unsigned char *>(result.data()));
+ SHA256Result(&copy, result.data());
break;
}
case QCryptographicHash::Sha384: {
SHA384Context copy = sha384Context;
result.resizeForOverwrite(SHA384HashSize);
- SHA384Result(&copy, reinterpret_cast<unsigned char *>(result.data()));
+ SHA384Result(&copy, result.data());
break;
}
case QCryptographicHash::Sha512: {
SHA512Context copy = sha512Context;
result.resizeForOverwrite(SHA512HashSize);
- SHA512Result(&copy, reinterpret_cast<unsigned char *>(result.data()));
+ SHA512Result(&copy, result.data());
break;
}
case QCryptographicHash::RealSha3_224:
case QCryptographicHash::RealSha3_256:
case QCryptographicHash::RealSha3_384:
case QCryptographicHash::RealSha3_512: {
- sha3Finish(8 * hashLengthInternal(method), Sha3Variant::Sha3);
+ sha3Finish(result, 8 * hashLengthInternal(method), Sha3Variant::Sha3);
break;
}
case QCryptographicHash::Keccak_224:
case QCryptographicHash::Keccak_256:
case QCryptographicHash::Keccak_384:
case QCryptographicHash::Keccak_512: {
- sha3Finish(8 * hashLengthInternal(method), Sha3Variant::Keccak);
+ sha3Finish(result, 8 * hashLengthInternal(method), Sha3Variant::Keccak);
break;
}
case QCryptographicHash::Blake2b_160:
@@ -619,7 +1128,7 @@ void QCryptographicHashPrivate::finalize() noexcept
const auto length = hashLengthInternal(method);
blake2b_state copy = blake2bContext;
result.resizeForOverwrite(length);
- blake2b_final(&copy, reinterpret_cast<uint8_t *>(result.data()), length);
+ blake2b_final(&copy, result.data(), length);
break;
}
case QCryptographicHash::Blake2s_128:
@@ -629,12 +1138,15 @@ void QCryptographicHashPrivate::finalize() noexcept
const auto length = hashLengthInternal(method);
blake2s_state copy = blake2sContext;
result.resizeForOverwrite(length);
- blake2s_final(&copy, reinterpret_cast<uint8_t *>(result.data()), length);
+ blake2s_final(&copy, result.data(), length);
break;
}
#endif
+ case QCryptographicHash::NumAlgorithms:
+ Q_UNREACHABLE();
}
}
+#endif // !USING_OPENSSL30
/*!
Returns the hash of \a data using \a method.
@@ -646,7 +1158,7 @@ QByteArray QCryptographicHash::hash(QByteArrayView data, Algorithm method)
{
QCryptographicHashPrivate hash(method);
hash.addData(data);
- hash.finalize();
+ hash.finalizeUnchecked(); // no mutex needed: no-one but us has access to 'hash'
return hash.resultView().toByteArray();
}
@@ -660,6 +1172,480 @@ int QCryptographicHash::hashLength(QCryptographicHash::Algorithm method)
return hashLengthInternal(method);
}
+/*!
+ Returns whether the selected algorithm \a method is supported and if
+ result() will return a value when the \a method is used.
+
+ \note OpenSSL will be responsible for providing this information when
+ used as a provider, otherwise \c true will be returned as the non-OpenSSL
+ implementation doesn't have any restrictions.
+ We return \c false if we fail to query OpenSSL.
+
+ \since 6.5
+*/
+
+
+bool QCryptographicHash::supportsAlgorithm(QCryptographicHash::Algorithm method)
+{
+ return QCryptographicHashPrivate::supportsAlgorithm(method);
+}
+
+bool QCryptographicHashPrivate::supportsAlgorithm(QCryptographicHash::Algorithm method)
+{
+#ifdef USING_OPENSSL30
+ // OpenSSL doesn't support Blake2b{60,236,384} and Blake2s{128,160,224}
+ // and these would automatically return FALSE in that case, while they are
+ // actually supported by our non-OpenSSL implementation.
+ if (useNonOpenSSLFallback(method))
+ return true;
+
+ 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));
+
+ return algorithm != nullptr;
+#else
+ switch (method) {
+ case QCryptographicHash::Sha1:
+#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ case QCryptographicHash::Md4:
+ case QCryptographicHash::Md5:
+ case QCryptographicHash::Sha224:
+ case QCryptographicHash::Sha256:
+ case QCryptographicHash::Sha384:
+ case QCryptographicHash::Sha512:
+ case QCryptographicHash::RealSha3_224:
+ case QCryptographicHash::Keccak_224:
+ case QCryptographicHash::RealSha3_256:
+ case QCryptographicHash::Keccak_256:
+ case QCryptographicHash::RealSha3_384:
+ case QCryptographicHash::Keccak_384:
+ case QCryptographicHash::RealSha3_512:
+ case QCryptographicHash::Keccak_512:
+ case QCryptographicHash::Blake2b_160:
+ case QCryptographicHash::Blake2b_256:
+ case QCryptographicHash::Blake2b_384:
+ case QCryptographicHash::Blake2b_512:
+ case QCryptographicHash::Blake2s_128:
+ case QCryptographicHash::Blake2s_160:
+ case QCryptographicHash::Blake2s_224:
+ case QCryptographicHash::Blake2s_256:
+#endif
+ return true;
+ case QCryptographicHash::NumAlgorithms: ;
+ };
+ return false;
+#endif // !USING_OPENSSL3
+}
+
+static constexpr int qt_hash_block_size(QCryptographicHash::Algorithm method)
+{
+ switch (method) {
+ case QCryptographicHash::Sha1:
+ return SHA1_Message_Block_Size;
+#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ case QCryptographicHash::Md4:
+ return 64;
+ case QCryptographicHash::Md5:
+ return 64;
+ case QCryptographicHash::Sha224:
+ return SHA224_Message_Block_Size;
+ case QCryptographicHash::Sha256:
+ return SHA256_Message_Block_Size;
+ case QCryptographicHash::Sha384:
+ return SHA384_Message_Block_Size;
+ case QCryptographicHash::Sha512:
+ return SHA512_Message_Block_Size;
+ case QCryptographicHash::RealSha3_224:
+ case QCryptographicHash::Keccak_224:
+ return 144;
+ case QCryptographicHash::RealSha3_256:
+ case QCryptographicHash::Keccak_256:
+ return 136;
+ case QCryptographicHash::RealSha3_384:
+ case QCryptographicHash::Keccak_384:
+ return 104;
+ case QCryptographicHash::RealSha3_512:
+ case QCryptographicHash::Keccak_512:
+ return 72;
+ case QCryptographicHash::Blake2b_160:
+ case QCryptographicHash::Blake2b_256:
+ case QCryptographicHash::Blake2b_384:
+ case QCryptographicHash::Blake2b_512:
+ return BLAKE2B_BLOCKBYTES;
+ case QCryptographicHash::Blake2s_128:
+ case QCryptographicHash::Blake2s_160:
+ case QCryptographicHash::Blake2s_224:
+ case QCryptographicHash::Blake2s_256:
+ return BLAKE2S_BLOCKBYTES;
+#endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1
+ case QCryptographicHash::NumAlgorithms:
+#if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 900
+ // GCC 8 has trouble with Q_UNREACHABLE() in constexpr functions
+ Q_UNREACHABLE();
+#endif
+ break;
+ }
+ return 0;
+}
+
+constexpr int maxHashBlockSize()
+{
+ int result = 0;
+ using A = QCryptographicHash::Algorithm;
+ for (int i = 0; i < A::NumAlgorithms ; ++i)
+ result = std::max(result, qt_hash_block_size(A(i)));
+ return result;
+}
+
+[[maybe_unused]]
+constexpr int minHashBlockSize()
+{
+ int result = INT_MAX;
+ using A = QCryptographicHash::Algorithm;
+ for (int i = 0; i < A::NumAlgorithms ; ++i)
+ result = std::min(result, qt_hash_block_size(A(i)));
+ return result;
+}
+
+[[maybe_unused]]
+constexpr int gcdHashBlockSize()
+{
+ int result = 0;
+ using A = QCryptographicHash::Algorithm;
+ for (int i = 0; i < A::NumAlgorithms ; ++i)
+ result = std::gcd(result, qt_hash_block_size(A(i)));
+ return result;
+}
+
+using HashBlock = QSmallByteArray<maxHashBlockSize()>;
+
+static HashBlock xored(const HashBlock &block, quint8 val) noexcept
+{
+ // some hints for the optimizer:
+ 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)
+ result[i] = block[i] ^ val;
+ return result;
+}
+
+class QMessageAuthenticationCodePrivate
+{
+public:
+ QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m)
+ : messageHash(m)
+ {
+ }
+
+ HashBlock key;
+ QCryptographicHashPrivate messageHash;
+
+ void setKey(QByteArrayView k) noexcept;
+ void initMessageHash() noexcept;
+ void finalize();
+
+ // when not called from the static hash() function, this function needs to be
+ // called with messageHash.finalizeMutex held:
+ void finalizeUnchecked() noexcept;
+ // END functions that need to be called with finalizeMutex held
+};
+
+/*!
+ \internal
+
+ Transforms key \a newKey into a block-sized format and stores it in member
+ \c key.
+
+ This function assumes it can use messageHash (i.e. it's in its initial
+ state (reset() has been called)).
+*/
+void QMessageAuthenticationCodePrivate::setKey(QByteArrayView newKey) noexcept
+{
+ const int blockSize = qt_hash_block_size(messageHash.method);
+
+ if (newKey.size() > blockSize) {
+ messageHash.addData(newKey);
+ messageHash.finalizeUnchecked();
+ static_assert([] {
+ using A = QCryptographicHash::Algorithm;
+ for (int i = 0; i < A::NumAlgorithms; ++i) {
+ if (hashLengthInternal(A(i)) > qt_hash_block_size(A(i)))
+ return false;
+ }
+ return true;
+ }(), "this code assumes that a hash's result always fits into that hash's block size");
+ key = messageHash.result;
+ messageHash.reset();
+ } else {
+ key.assign(newKey);
+ }
+
+ if (key.size() < blockSize)
+ key.resize(blockSize, '\0');
+
+ initMessageHash();
+}
+
+/*!
+ \internal
+
+ Seeds messageHash from \c key.
+
+ This function assumes that messageHash is in its initial state (reset() has
+ been called).
+*/
+void QMessageAuthenticationCodePrivate::initMessageHash() noexcept
+{
+ messageHash.addData(xored(key, 0x36));
+}
+
+/*!
+ \class QMessageAuthenticationCode
+ \inmodule QtCore
+
+ \brief The QMessageAuthenticationCode class provides a way to generate
+ hash-based message authentication codes.
+
+ \since 5.1
+
+ \ingroup tools
+ \reentrant
+
+ 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:
+
+ \snippet qmessageauthenticationcode/main.cpp 0
+ \dots
+ \snippet qmessageauthenticationcode/main.cpp 1
+
+ For simple cases like above, you can also use the static
+ \l hash() function:
+
+ \snippet qmessageauthenticationcode/main.cpp 2
+
+
+ \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
+*/
+
+/*!
+ Constructs an object that can be used to create a cryptographic hash from data
+ using method \a method and key \a key.
+
+//! [qba-to-qbav-6.6]
+ \note In Qt versions prior to 6.6, this function took its arguments as
+ QByteArray, not QByteArrayView. If you experience compile errors, it's
+ because your code is passing objects that are implicitly convertible to
+ QByteArray, but not QByteArrayView. Wrap the corresponding argument in
+ \c{QByteArray{~~~}} to make the cast explicit. This is backwards-compatible
+ with old Qt versions.
+//! [qba-to-qbav-6.6]
+*/
+QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method,
+ QByteArrayView key)
+ : d(new QMessageAuthenticationCodePrivate(method))
+{
+ d->setKey(key);
+}
+
+/*!
+ Destroys the object.
+*/
+QMessageAuthenticationCode::~QMessageAuthenticationCode()
+{
+ delete d;
+}
+
+/*!
+ \fn QMessageAuthenticationCode::QMessageAuthenticationCode(QMessageAuthenticationCode &&other)
+
+ Move-constructs a new QMessageAuthenticationCode from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new object.
+
+ \since 6.6
+*/
+
+/*!
+ \fn QMessageAuthenticationCode &QMessageAuthenticationCode::operator=(QMessageAuthenticationCode &&other)
+
+ Move-assigns \a other to this QMessageAuthenticationCode instance.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new object.
+
+ \since 6.6
+*/
+
+/*!
+ \fn void QMessageAuthenticationCode::swap(QMessageAuthenticationCode &other)
+
+ Swaps message authentication code \a other with this message authentication
+ code. This operation is very fast and never fails.
+
+ \since 6.6
+*/
+
+/*!
+ Resets message data. Calling this function doesn't affect the key.
+*/
+void QMessageAuthenticationCode::reset() noexcept
+{
+ d->messageHash.reset();
+ d->initMessageHash();
+}
+
+/*!
+ Sets secret \a key. Calling this function automatically resets the object state.
+
+ For optimal performance, call this function only to \e change the active key,
+ not to set an \e initial key, as in
+
+ \code
+ QMessageAuthenticationCode mac(method);
+ mac.setKey(key); // does extra work
+ use(mac);
+ \endcode
+
+ Prefer to pass initial keys as the constructor argument:
+
+ \code
+ QMessageAuthenticationCode mac(method, key); // OK, optimal
+ use(mac);
+ \endcode
+
+ You can use std::optional to delay construction of a
+ QMessageAuthenticationCode until you know the key:
+
+ \code
+ std::optional<QMessageAuthenticationCode> mac;
+ ~~~
+ key = ~~~;
+ mac.emplace(method, key);
+ use(*mac);
+ \endcode
+
+ \include qcryptographichash.cpp {qba-to-qbav-6.6}
+*/
+void QMessageAuthenticationCode::setKey(QByteArrayView key) noexcept
+{
+ d->messageHash.reset();
+ d->setKey(key);
+}
+
+/*!
+ \overload
+ Adds the first \a length chars of \a data to the message.
+*/
+void QMessageAuthenticationCode::addData(const char *data, qsizetype length)
+{
+ d->messageHash.addData({data, length});
+}
+
+/*!
+ Adds \a data to the message.
+
+ \include qcryptographichash.cpp {qba-to-qbav-6.6}
+
+ \sa resultView(), result()
+*/
+void QMessageAuthenticationCode::addData(QByteArrayView data) noexcept
+{
+ d->messageHash.addData(data);
+}
+
+/*!
+ Reads the data from the open QIODevice \a device until it ends
+ and adds it to message. Returns \c true if reading was successful.
+
+ \note \a device must be already opened.
+ */
+bool QMessageAuthenticationCode::addData(QIODevice *device)
+{
+ return d->messageHash.addData(device);
+}
+
+/*!
+ \since 6.6
+
+ Returns the final hash value.
+
+ Note that the returned view remains valid only as long as the
+ QMessageAuthenticationCode object is not modified by other means.
+
+ \sa result()
+*/
+QByteArrayView QMessageAuthenticationCode::resultView() const noexcept
+{
+ d->finalize();
+ return d->messageHash.resultView();
+}
+
+/*!
+ Returns the final authentication code.
+
+ \sa resultView(), QByteArray::toHex()
+*/
+QByteArray QMessageAuthenticationCode::result() const
+{
+ return resultView().toByteArray();
+}
+
+void QMessageAuthenticationCodePrivate::finalize()
+{
+ const auto lock = qt_scoped_lock(messageHash.finalizeMutex);
+ if (!messageHash.result.isEmpty())
+ return;
+ finalizeUnchecked();
+}
+
+void QMessageAuthenticationCodePrivate::finalizeUnchecked() noexcept
+{
+ messageHash.finalizeUnchecked();
+ const HashResult hashedMessage = messageHash.result;
+
+ messageHash.reset();
+ messageHash.addData(xored(key, 0x5c));
+ messageHash.addData(hashedMessage);
+ messageHash.finalizeUnchecked();
+}
+
+/*!
+ Returns the authentication code for the message \a message using
+ the key \a key and the method \a method.
+
+ \include qcryptographichash.cpp {qba-to-qbav-6.6}
+*/
+QByteArray QMessageAuthenticationCode::hash(QByteArrayView message, QByteArrayView key,
+ QCryptographicHash::Algorithm method)
+{
+ QMessageAuthenticationCodePrivate mac(method);
+ mac.setKey(key);
+ mac.messageHash.addData(message);
+ mac.finalizeUnchecked();
+ return mac.messageHash.resultView().toByteArray();
+}
+
QT_END_NAMESPACE
#ifndef QT_NO_QOBJECT
diff --git a/src/corelib/tools/qcryptographichash.h b/src/corelib/tools/qcryptographichash.h
index f5710015cf..294453adce 100644
--- a/src/corelib/tools/qcryptographichash.h
+++ b/src/corelib/tools/qcryptographichash.h
@@ -60,13 +60,19 @@ public:
Blake2s_224,
Blake2s_256,
#endif
+ NumAlgorithms
};
Q_ENUM(Algorithm)
explicit QCryptographicHash(Algorithm method);
+ QCryptographicHash(QCryptographicHash &&other) noexcept : d(std::exchange(other.d, nullptr)) {}
~QCryptographicHash();
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QCryptographicHash)
+ void swap(QCryptographicHash &other) noexcept { qt_ptr_swap(d, other.d); }
+
void reset() noexcept;
+ [[nodiscard]] Algorithm algorithm() const noexcept;
#if QT_DEPRECATED_SINCE(6, 4)
QT_DEPRECATED_VERSION_X_6_4("Use the QByteArrayView overload instead")
@@ -86,6 +92,7 @@ public:
#endif
static QByteArray hash(QByteArrayView data, Algorithm method);
static int hashLength(Algorithm method);
+ static bool supportsAlgorithm(Algorithm method);
private:
Q_DISABLE_COPY(QCryptographicHash)
QCryptographicHashPrivate *d;
diff --git a/src/corelib/tools/qduplicatetracker_p.h b/src/corelib/tools/qduplicatetracker_p.h
index 4d0e6e4777..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
@@ -56,13 +56,13 @@ class QDuplicateTracker {
auto insert(const T &e) {
auto it = QSet<T>::insert(e);
const auto n = this->size();
- return std::pair{it, qExchange(setSize, n) != n};
+ return std::pair{it, std::exchange(setSize, n) != n};
}
auto insert(T &&e) {
auto it = QSet<T>::insert(std::move(e));
const auto n = this->size();
- return std::pair{it, qExchange(setSize, n) != n};
+ return std::pair{it, std::exchange(setSize, n) != n};
}
};
Set set{Prealloc};
diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp
index 9242a617ba..d8b3367de3 100644
--- a/src/corelib/tools/qeasingcurve.cpp
+++ b/src/corelib/tools/qeasingcurve.cpp
@@ -447,7 +447,7 @@ struct BezierEase : public QEasingCurveFunction
{
if (_bezierCurves.constLast() == QPointF(1.0, 1.0)) {
_init = true;
- _curveCount = _bezierCurves.count() / 3;
+ _curveCount = _bezierCurves.size() / 3;
for (int i=0; i < _curveCount; i++) {
_intervals[i] = _bezierCurves.at(i * 3 + 2).x();
@@ -466,17 +466,17 @@ struct BezierEase : public QEasingCurveFunction
_curves[0].p3y = _bezierCurves.at(2).y();
} else if (i == (_curveCount - 1)) {
- _curves[i].p0x = _bezierCurves.at(_bezierCurves.count() - 4).x();
- _curves[i].p0y = _bezierCurves.at(_bezierCurves.count() - 4).y();
+ _curves[i].p0x = _bezierCurves.at(_bezierCurves.size() - 4).x();
+ _curves[i].p0y = _bezierCurves.at(_bezierCurves.size() - 4).y();
- _curves[i].p1x = _bezierCurves.at(_bezierCurves.count() - 3).x();
- _curves[i].p1y = _bezierCurves.at(_bezierCurves.count() - 3).y();
+ _curves[i].p1x = _bezierCurves.at(_bezierCurves.size() - 3).x();
+ _curves[i].p1y = _bezierCurves.at(_bezierCurves.size() - 3).y();
- _curves[i].p2x = _bezierCurves.at(_bezierCurves.count() - 2).x();
- _curves[i].p2y = _bezierCurves.at(_bezierCurves.count() - 2).y();
+ _curves[i].p2x = _bezierCurves.at(_bezierCurves.size() - 2).x();
+ _curves[i].p2y = _bezierCurves.at(_bezierCurves.size() - 2).y();
- _curves[i].p3x = _bezierCurves.at(_bezierCurves.count() - 1).x();
- _curves[i].p3y = _bezierCurves.at(_bezierCurves.count() - 1).y();
+ _curves[i].p3x = _bezierCurves.at(_bezierCurves.size() - 1).x();
+ _curves[i].p3y = _bezierCurves.at(_bezierCurves.size() - 1).y();
} else {
_curves[i].p0x = _bezierCurves.at(i * 3 - 1).x();
_curves[i].p0y = _bezierCurves.at(i * 3 - 1).y();
@@ -535,7 +535,7 @@ struct BezierEase : public QEasingCurveFunction
qreal value(qreal x) override
{
- Q_ASSERT(_bezierCurves.count() % 3 == 0);
+ Q_ASSERT(_bezierCurves.size() % 3 == 0);
if (_bezierCurves.isEmpty()) {
return x;
@@ -869,7 +869,7 @@ struct TCBEase : public BezierEase
qreal value(qreal x) override
{
- Q_ASSERT(_bezierCurves.count() % 3 == 0);
+ Q_ASSERT(_bezierCurves.size() % 3 == 0);
if (_bezierCurves.isEmpty()) {
qWarning("QEasingCurve: Invalid tcb curve");
@@ -1274,7 +1274,7 @@ void QEasingCurve::addCubicBezierSegment(const QPointF & c1, const QPointF & c2,
QList<QPointF> static inline tcbToBezier(const TCBPoints &tcbPoints)
{
- const int count = tcbPoints.count();
+ const int count = tcbPoints.size();
QList<QPointF> bezierPoints;
bezierPoints.reserve(3 * (count - 1));
diff --git a/src/corelib/tools/qflatmap_p.h b/src/corelib/tools/qflatmap_p.h
index 28bb72c864..d2c0d45b79 100644
--- a/src/corelib/tools/qflatmap_p.h
+++ b/src/corelib/tools/qflatmap_p.h
@@ -74,7 +74,7 @@ public:
using value_type = std::pair<const Key, T>;
static constexpr bool is_comparator_noexcept = noexcept(
- std::declval<Compare>()(std::declval<Key>(), std::declval<Key>()));
+ std::declval<Compare>()(std::declval<const Key &>(), std::declval<const Key &>()));
bool operator()(const value_type &lhs, const value_type &rhs) const
noexcept(is_comparator_noexcept)
@@ -83,27 +83,34 @@ public:
}
};
+namespace qflatmap {
+namespace detail {
+template <class T>
+class QFlatMapMockPointer
+{
+ T ref;
+public:
+ QFlatMapMockPointer(T r)
+ : ref(r)
+ {
+ }
+
+ T *operator->()
+ {
+ return &ref;
+ }
+};
+} // namespace detail
+} // namespace qflatmap
+
template<class Key, class T, class Compare = std::less<Key>, class KeyContainer = QList<Key>,
class MappedContainer = QList<T>>
class QFlatMap : private QFlatMapValueCompare<Key, T, Compare>
{
static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
- template <class U>
- class mock_pointer
- {
- U ref;
- public:
- mock_pointer(U r)
- : ref(r)
- {
- }
-
- U *operator->()
- {
- return &ref;
- }
- };
+ template<class U>
+ using mock_pointer = qflatmap::detail::QFlatMapMockPointer<U>;
public:
using key_type = Key;
@@ -844,7 +851,6 @@ public:
size_type remove_if(Predicate pred)
{
const auto indirect_call_to_pred = [pred = std::move(pred)](iterator it) {
- [[maybe_unused]] auto dependent_false = [](auto &&...) { return false; };
using Pair = decltype(*it);
using K = decltype(it.key());
using V = decltype(it.value());
@@ -856,7 +862,7 @@ public:
} else if constexpr (std::is_invocable_v<P, K> && !std::is_invocable_v<P, Pair>) {
return pred(it.key());
} else {
- static_assert(dependent_false(pred),
+ static_assert(QtPrivate::type_dependent_false<Predicate>(),
"Don't know how to call the predicate.\n"
"Options:\n"
"- pred(*it)\n"
@@ -1093,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/qfreelist_p.h b/src/corelib/tools/qfreelist_p.h
index c06c569d23..6bbde5b4b6 100644
--- a/src/corelib/tools/qfreelist_p.h
+++ b/src/corelib/tools/qfreelist_p.h
@@ -124,8 +124,7 @@ class QFreeList
return i;
x -= size;
}
- Q_UNREACHABLE();
- return -1;
+ Q_UNREACHABLE_RETURN(-1);
}
// allocate a block of the given \a size, initialized starting with the given \a offset
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
new file mode 100644
index 0000000000..0942d5fe7d
--- /dev/null
+++ b/src/corelib/tools/qfunctionaltools_impl.h
@@ -0,0 +1,77 @@
+// 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
+
+#if 0
+#pragma qt_sync_skip_header_check
+#pragma qt_sync_stop_processing
+#endif
+
+#ifndef QFUNCTIONALTOOLS_IMPL_H
+#define QFUNCTIONALTOOLS_IMPL_H
+
+#include <QtCore/qtconfigmacros.h>
+
+#include <type_traits>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtPrivate {
+
+namespace detail {
+
+#define FOR_EACH_CVREF(op) \
+ op(&) \
+ op(const &) \
+ op(&&) \
+ op(const &&) \
+ /* end */
+
+
+template <typename Object, typename = void>
+struct StorageByValue
+{
+ Object o;
+#define MAKE_GETTER(cvref) \
+ constexpr Object cvref object() cvref noexcept \
+ { return static_cast<Object cvref>(o); }
+ FOR_EACH_CVREF(MAKE_GETTER)
+#undef MAKE_GETTER
+};
+
+template <typename Object, typename Tag = void>
+struct StorageEmptyBaseClassOptimization : Object
+{
+ StorageEmptyBaseClassOptimization() = default;
+ StorageEmptyBaseClassOptimization(Object &&o)
+ : Object(std::move(o))
+ {}
+ StorageEmptyBaseClassOptimization(const Object &o)
+ : Object(o)
+ {}
+
+#define MAKE_GETTER(cvref) \
+ constexpr Object cvref object() cvref noexcept \
+ { return static_cast<Object cvref>(*this); }
+ FOR_EACH_CVREF(MAKE_GETTER)
+#undef MAKE_GETTER
+};
+} // namespace detail
+
+template <typename Object, typename Tag = void>
+using CompactStorage = typename std::conditional_t<
+ std::conjunction_v<
+ std::is_empty<Object>,
+ std::negation<std::is_final<Object>>
+ >,
+ detail::StorageEmptyBaseClassOptimization<Object, Tag>,
+ detail::StorageByValue<Object, Tag>
+ >;
+
+} // namespace QtPrivate
+
+#undef FOR_EACH_CVREF
+
+QT_END_NAMESPACE
+
+#endif // QFUNCTIONALTOOLS_IMPL_H
diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp
index c973eaeabe..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()
@@ -107,26 +111,22 @@ private:
StateResult result = { 0, OverriddenByEnvironment };
#ifdef QT_BOOTSTRAPPED
Q_UNUSED(which);
- Q_UNREACHABLE();
+ Q_UNREACHABLE_RETURN(result);
#else
// can't use qEnvironmentVariableIntValue (reentrancy)
const char *seedstr = getenv("QT_HASH_SEED");
- const char *endptr = nullptr;
- bool ok = false;
- int seed = 0;
- if (seedstr)
- seed = qstrntoll(seedstr, strlen(seedstr), &endptr, 10, &ok);
- if (ok && endptr != seedstr + strlen(seedstr))
- ok = false;
- if (ok) {
- if (seed) {
- // can't use qWarning here (reentrancy)
- fprintf(stderr, "QT_HASH_SEED: forced seed value is not 0; ignored.\n");
+ if (seedstr) {
+ auto r = qstrntoll(seedstr, strlen(seedstr), 10);
+ if (r.used > 0 && size_t(r.used) == strlen(seedstr)) {
+ if (r.result) {
+ // can't use qWarning here (reentrancy)
+ fprintf(stderr, "QT_HASH_SEED: forced seed value is not 0; ignored.\n");
+ }
+
+ // we don't have to store to the seed, since it's pre-initialized by
+ // the compiler to zero
+ return result;
}
-
- // we don't have to store to the seed, since it's pre-initialized by
- // the compiler to zero
- return result;
}
// update the full seed
@@ -137,8 +137,8 @@ private:
result.requestedSeed = x.data[i];
}
result.state = JustInitialized;
-#endif
return result;
+#endif
}
inline HashSeedStorage::StateResult HashSeedStorage::state(int which)
@@ -285,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 { \
@@ -315,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;
@@ -324,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;
@@ -344,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;
@@ -384,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.
@@ -395,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 { \
@@ -420,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;
@@ -429,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;
@@ -449,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;
@@ -480,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
@@ -527,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);
@@ -540,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);
@@ -591,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);
}
@@ -611,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
@@ -620,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);
}
@@ -650,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);
@@ -687,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);
@@ -698,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);
@@ -710,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);
}
@@ -737,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
@@ -937,32 +1098,20 @@ 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);
-}
-
-size_t qHash(const QByteArray &key, size_t seed) noexcept
-{
- return qHashBits(key.constData(), size_t(key.size()), seed);
+ return qHashBits_fallback<>(data, size, seed, seed2);
}
-size_t qHash(const QByteArrayView &key, size_t seed) noexcept
+size_t qHash(QByteArrayView key, size_t seed) noexcept
{
return qHashBits(key.constData(), size_t(key.size()), seed);
}
@@ -972,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;
@@ -984,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);
}
/*!
@@ -1392,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
@@ -1420,7 +1610,7 @@ 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(float key, size_t seed) noexcept
+/*! \fn size_t qHash(float key, size_t seed = 0) noexcept
\relates QHash
\since 5.3
@@ -1445,7 +1635,6 @@ size_t qHash(double key, size_t seed) noexcept
}
}
-#if !defined(Q_OS_DARWIN) || defined(Q_CLANG_QDOC)
/*! \relates QHash
\since 5.3
@@ -1463,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
@@ -1521,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
@@ -1649,7 +1837,7 @@ size_t qHash(long double key, size_t seed) noexcept
hash table, use \l{QMultiHash}.
If you only need to extract the values from a hash (not the keys),
- you can also use \l{foreach}:
+ you can also use range-based for:
\snippet code/src_corelib_tools_qhash.cpp 12
@@ -1688,10 +1876,15 @@ size_t qHash(long double key, size_t seed) noexcept
The two-arguments overloads take an unsigned integer that should be used to
seed the calculation of the hash function. This seed is provided by QHash
- in order to prevent a family of \l{algorithmic complexity attacks}. If both
- a one-argument and a two-arguments overload are defined for a key type,
- the latter is used by QHash (note that you can simply define a
- two-arguments version, and use a default value for the seed parameter).
+ in order to prevent a family of \l{algorithmic complexity attacks}.
+
+ \note In Qt 6 it is possible to define a \c{qHash()} overload
+ taking only one argument; support for this is deprecated. Starting
+ with Qt 7, it will be mandatory to use a two-arguments overload. If
+ both a one-argument and a two-arguments overload are defined for a
+ key type, the latter is used by QHash (note that you can simply
+ define a two-arguments version, and use a default value for the
+ seed parameter).
The second way to provide a hashing function is by specializing
the \c{std::hash} class for the key type \c{K}, and providing a
@@ -1787,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.
*/
@@ -2023,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
@@ -2046,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()
*/
@@ -2129,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
@@ -2143,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()
*/
@@ -2151,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()
*/
@@ -2160,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()
*/
@@ -2168,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
@@ -2181,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()
*/
@@ -2190,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()
*/
@@ -2199,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()
*/
@@ -2208,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()
*/
@@ -2217,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()
*/
@@ -2226,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()
*/
@@ -2235,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()
*/
@@ -2244,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()
*/
@@ -2253,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()
*/
@@ -2272,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
*/
@@ -2289,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()
*/
@@ -2308,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
@@ -2325,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()
*/
@@ -2334,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
*/
/*!
@@ -2345,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
*/
@@ -2364,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
@@ -2471,12 +2722,6 @@ size_t qHash(long double key, size_t seed) noexcept
\inmodule QtCore
\brief The QHash::iterator class provides an STL-style non-const iterator for QHash.
- QHash features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QHash\<Key, T\>::iterator allows you to iterate over a QHash
and to modify the value (but not the key) associated
with a particular key. If you want to iterate over a const QHash,
@@ -2497,31 +2742,15 @@ size_t qHash(long double key, size_t seed) noexcept
Unlike QMap, which orders its items by key, QHash stores its
items in an arbitrary order.
- Let's see a few examples of things we can do with a
- QHash::iterator that we cannot do with a QHash::const_iterator.
Here's an example that increments every value stored in the QHash
by 2:
\snippet code/src_corelib_tools_qhash.cpp 18
- Here's an example that removes all the items whose key is a
- string that starts with an underscore character:
-
- \snippet code/src_corelib_tools_qhash.cpp 19
-
- The call to QHash::erase() removes the item pointed to by the
- iterator from the hash, and returns an iterator to the next item.
- Here's another way of removing an item while iterating:
-
- \snippet code/src_corelib_tools_qhash.cpp 20
-
- It might be tempting to write code like this:
+ To remove elements from a QHash you can use erase_if(QHash\<Key, T\> &map, Predicate pred):
\snippet code/src_corelib_tools_qhash.cpp 21
- However, this will potentially crash in \c{++i}, because \c i is
- a dangling iterator after the call to erase().
-
Multiple iterators can be used on the same hash. However, be aware
that any modification performed directly on the QHash (inserting and
removing items) can cause the iterators to become invalid.
@@ -2532,10 +2761,6 @@ size_t qHash(long double key, size_t seed) noexcept
to grow/shrink its internal hash table.
Using any iterator after a rehashing operation has occurred will lead to undefined behavior.
- You can however safely use iterators to remove entries from the hash
- using the QHash::erase() method. This function can safely be called while
- iterating, and won't affect the order of items in the hash.
-
If you need to keep iterators over a long period of time, we recommend
that you use QMap rather than QHash.
@@ -2544,7 +2769,7 @@ size_t qHash(long double key, size_t seed) noexcept
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QHash::const_iterator, QHash::key_iterator, QMutableHashIterator
+ \sa QHash::const_iterator, QHash::key_iterator, QHash::key_value_iterator
*/
/*! \fn template <class Key, class T> QHash<Key, T>::iterator::iterator()
@@ -2640,12 +2865,6 @@ size_t qHash(long double key, size_t seed) noexcept
\inmodule QtCore
\brief The QHash::const_iterator class provides an STL-style const iterator for QHash.
- QHash features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QHash\<Key, T\>::const_iterator allows you to iterate over a
QHash. If you want to modify the QHash as you
iterate over it, you must use QHash::iterator instead. It is
@@ -2656,8 +2875,8 @@ size_t qHash(long double key, size_t seed) noexcept
The default QHash::const_iterator constructor creates an
uninitialized iterator. You must initialize it using a QHash
- function like QHash::constBegin(), QHash::constEnd(), or
- QHash::find() before you can start iterating. Here's a typical
+ function like QHash::cbegin(), QHash::cend(), or
+ QHash::constFind() before you can start iterating. Here's a typical
loop that prints all the (key, value) pairs stored in a hash:
\snippet code/src_corelib_tools_qhash.cpp 23
@@ -2678,12 +2897,16 @@ size_t qHash(long double key, size_t seed) noexcept
to grow/shrink its internal hash table.
Using any iterator after a rehashing operation has occurred will lead to undefined behavior.
+ You can however safely use iterators to remove entries from the hash
+ using the QHash::erase() method. This function can safely be called while
+ iterating, and won't affect the order of items in the hash.
+
\warning Iterators on implicitly shared containers do not work
exactly like STL-iterators. You should avoid copying a container
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QHash::iterator, QHashIterator
+ \sa QHash::iterator, QHash::key_iterator, QHash::const_key_value_iterator
*/
/*! \fn template <class Key, class T> QHash<Key, T>::const_iterator::const_iterator()
@@ -2969,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)
@@ -2985,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.
*/
@@ -3001,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()
*/
@@ -3013,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()
*/
@@ -3031,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
*/
@@ -3047,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
*/
@@ -3113,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()
*/
@@ -3265,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
*/
/*!
@@ -3282,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()
@@ -3289,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
@@ -3303,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()
*/
@@ -3311,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()
*/
@@ -3320,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()
*/
@@ -3328,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()
*/
@@ -3341,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()
*/
@@ -3350,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()
*/
@@ -3359,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()
*/
@@ -3368,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()
*/
@@ -3377,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()
*/
@@ -3386,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()
*/
@@ -3395,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()
*/
@@ -3404,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()
*/
@@ -3413,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()
*/
@@ -3432,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
*/
@@ -3439,12 +3711,6 @@ size_t qHash(long double key, size_t seed) noexcept
\inmodule QtCore
\brief The QMultiHash::iterator class provides an STL-style non-const iterator for QMultiHash.
- QMultiHash features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QMultiHash\<Key, T\>::iterator allows you to iterate over a QMultiHash
and to modify the value (but not the key) associated
with a particular key. If you want to iterate over a const QMultiHash,
@@ -3465,31 +3731,15 @@ size_t qHash(long double key, size_t seed) noexcept
Unlike QMap, which orders its items by key, QMultiHash stores its
items in an arbitrary order.
- Let's see a few examples of things we can do with a
- QMultiHash::iterator that we cannot do with a QMultiHash::const_iterator.
Here's an example that increments every value stored in the QMultiHash
by 2:
\snippet code/src_corelib_tools_qhash.cpp 18
- Here's an example that removes all the items whose key is a
- string that starts with an underscore character:
-
- \snippet code/src_corelib_tools_qhash.cpp 19
-
- The call to QMultiHash::erase() removes the item pointed to by the
- iterator from the hash, and returns an iterator to the next item.
- Here's another way of removing an item while iterating:
-
- \snippet code/src_corelib_tools_qhash.cpp 20
-
- It might be tempting to write code like this:
+ To remove elements from a QMultiHash you can use erase_if(QMultiHash\<Key, T\> &map, Predicate pred):
\snippet code/src_corelib_tools_qhash.cpp 21
- However, this will potentially crash in \c{++i}, because \c i is
- a dangling iterator after the call to erase().
-
Multiple iterators can be used on the same hash. However, be aware
that any modification performed directly on the QHash (inserting and
removing items) can cause the iterators to become invalid.
@@ -3500,10 +3750,6 @@ size_t qHash(long double key, size_t seed) noexcept
to grow/shrink its internal hash table.
Using any iterator after a rehashing operation has occurred will lead to undefined behavior.
- You can however safely use iterators to remove entries from the hash
- using the QHash::erase() method. This function can safely be called while
- iterating, and won't affect the order of items in the hash.
-
If you need to keep iterators over a long period of time, we recommend
that you use QMultiMap rather than QHash.
@@ -3512,7 +3758,7 @@ size_t qHash(long double key, size_t seed) noexcept
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QMultiHash::const_iterator, QMultiHash::key_iterator, QMutableHashIterator
+ \sa QMultiHash::const_iterator, QMultiHash::key_iterator, QMultiHash::key_value_iterator
*/
/*! \fn template <class Key, class T> QMultiHash<Key, T>::iterator::iterator()
@@ -3608,12 +3854,6 @@ size_t qHash(long double key, size_t seed) noexcept
\inmodule QtCore
\brief The QMultiHash::const_iterator class provides an STL-style const iterator for QMultiHash.
- QMultiHash features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QMultiHash\<Key, T\>::const_iterator allows you to iterate over a
QMultiHash. If you want to modify the QMultiHash as you
iterate over it, you must use QMultiHash::iterator instead. It is
@@ -3624,8 +3864,8 @@ size_t qHash(long double key, size_t seed) noexcept
The default QMultiHash::const_iterator constructor creates an
uninitialized iterator. You must initialize it using a QMultiHash
- function like QMultiHash::constBegin(), QMultiHash::constEnd(), or
- QMultiHash::find() before you can start iterating. Here's a typical
+ function like QMultiHash::cbegin(), QMultiHash::cend(), or
+ QMultiHash::constFind() before you can start iterating. Here's a typical
loop that prints all the (key, value) pairs stored in a hash:
\snippet code/src_corelib_tools_qhash.cpp 23
@@ -3637,28 +3877,24 @@ size_t qHash(long double key, size_t seed) noexcept
recently to the least recently inserted value.
Multiple iterators can be used on the same hash. However, be aware
- that any modification performed directly on the QHash (inserting and
+ that any modification performed directly on the QMultiHash (inserting and
removing items) can cause the iterators to become invalid.
- Inserting items into the hash or calling methods such as QHash::reserve()
- or QHash::squeeze() can invalidate all iterators pointing into the hash.
- Iterators are guaranteed to stay valid only as long as the QHash doesn't have
+ Inserting items into the hash or calling methods such as QMultiHash::reserve()
+ or QMultiHash::squeeze() can invalidate all iterators pointing into the hash.
+ Iterators are guaranteed to stay valid only as long as the QMultiHash doesn't have
to grow/shrink it's internal hash table.
Using any iterator after a rehashing operation ahs occurred will lead to undefined behavior.
- You can however safely use iterators to remove entries from the hash
- using the QHash::erase() method. This function can safely be called while
- iterating, and won't affect the order of items in the hash.
-
If you need to keep iterators over a long period of time, we recommend
- that you use QMap rather than QHash.
+ that you use QMultiMap rather than QMultiHash.
\warning Iterators on implicitly shared containers do not work
exactly like STL-iterators. You should avoid copying a container
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QMultiHash::iterator
+ \sa QMultiHash::iterator, QMultiHash::key_iterator, QMultiHash::const_key_value_iterator
*/
/*! \fn template <class Key, class T> QMultiHash<Key, T>::const_iterator::const_iterator()
@@ -3922,4 +4158,36 @@ size_t qHash(long double key, size_t seed) noexcept
Returns the number of elements removed, if any.
*/
+#ifdef QT_HAS_CONSTEXPR_BITOPS
+namespace QHashPrivate {
+static_assert(qPopulationCount(SpanConstants::NEntries) == 1,
+ "NEntries must be a power of 2 for bucketForHash() to work.");
+
+// ensure the size of a Span does not depend on the template parameters
+using Node1 = Node<int, int>;
+static_assert(sizeof(Span<Node1>) == sizeof(Span<Node<char, void *>>));
+static_assert(sizeof(Span<Node1>) == sizeof(Span<Node<qsizetype, QHashDummyValue>>));
+static_assert(sizeof(Span<Node1>) == sizeof(Span<Node<QString, QVariant>>));
+static_assert(sizeof(Span<Node1>) > SpanConstants::NEntries);
+static_assert(qNextPowerOfTwo(sizeof(Span<Node1>)) == SpanConstants::NEntries * 2);
+
+// ensure allocations are always a power of two, at a minimum NEntries,
+// obeying the fomula
+// qNextPowerOfTwo(2 * N);
+// without overflowing
+static constexpr size_t NEntries = SpanConstants::NEntries;
+static_assert(GrowthPolicy::bucketsForCapacity(1) == NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(NEntries / 2 + 0) == NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(NEntries / 2 + 1) == 2 * NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(NEntries * 1 - 1) == 2 * NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(NEntries * 1 + 0) == 4 * NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(NEntries * 1 + 1) == 4 * NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(NEntries * 2 - 1) == 4 * NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(NEntries * 2 + 0) == 8 * NEntries);
+static_assert(GrowthPolicy::bucketsForCapacity(SIZE_MAX / 4) == SIZE_MAX / 2 + 1);
+static_assert(GrowthPolicy::bucketsForCapacity(SIZE_MAX / 2) == SIZE_MAX);
+static_assert(GrowthPolicy::bucketsForCapacity(SIZE_MAX) == SIZE_MAX);
+}
+#endif
+
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h
index 5075459531..e7cd4123fb 100644
--- a/src/corelib/tools/qhash.h
+++ b/src/corelib/tools/qhash.h
@@ -5,11 +5,11 @@
#ifndef QHASH_H
#define QHASH_H
+#include <QtCore/qalgorithms.h>
#include <QtCore/qcontainertools_impl.h>
#include <QtCore/qhashfunctions.h>
#include <QtCore/qiterator.h>
#include <QtCore/qlist.h>
-#include <QtCore/qmath.h>
#include <QtCore/qrefcount.h>
#include <initializer_list>
@@ -172,7 +172,7 @@ struct MultiNode
MultiNode(MultiNode &&other)
: key(other.key),
- value(qExchange(other.value, nullptr))
+ value(std::exchange(other.value, nullptr))
{
}
@@ -203,7 +203,7 @@ struct MultiNode
void insertMulti(Args &&... args)
{
Chain *e = new Chain{ T(std::forward<Args>(args)...), nullptr };
- e->next = qExchange(value, e);
+ e->next = std::exchange(value, e);
}
template<typename ...Args>
void emplaceValue(Args &&... args)
@@ -414,28 +414,25 @@ struct Span {
// QHash uses a power of two growth policy.
namespace GrowthPolicy {
-inline constexpr size_t maxNumBuckets() noexcept
-{
- // ensure the size of a Span does not depend on the template parameters
- using Node1 = Node<int, int>;
- using Node2 = Node<char, void *>;
- using Node3 = Node<qsizetype, QHashDummyValue>;
- static_assert(sizeof(Span<Node1>) == sizeof(Span<Node2>));
- static_assert(sizeof(Span<Node1>) == sizeof(Span<Node3>));
-
- // Maximum is 2^31-1 or 2^63-1 bytes (limited by qsizetype and ptrdiff_t)
- size_t max = (std::numeric_limits<ptrdiff_t>::max)();
- return max / sizeof(Span<Node1>) * SpanConstants::NEntries;
-}
inline constexpr size_t bucketsForCapacity(size_t requestedCapacity) noexcept
{
+ constexpr int SizeDigits = std::numeric_limits<size_t>::digits;
+
// We want to use at minimum a full span (128 entries), so we hardcode it for any requested
// capacity <= 64. Any capacity above that gets rounded to a later power of two.
if (requestedCapacity <= 64)
return SpanConstants::NEntries;
- if (requestedCapacity >= maxNumBuckets())
- return maxNumBuckets();
- return qNextPowerOfTwo(QIntegerForSize<sizeof(size_t)>::Unsigned(2 * requestedCapacity - 1));
+
+ // Same as
+ // qNextPowerOfTwo(2 * requestedCapacity);
+ //
+ // but ensuring neither our multiplication nor the function overflow.
+ // Additionally, the maximum memory allocation is 2^31-1 or 2^63-1 bytes
+ // (limited by qsizetype and ptrdiff_t).
+ int count = qCountLeadingZeroBits(requestedCapacity);
+ if (count < 2)
+ return (std::numeric_limits<size_t>::max)(); // will cause std::bad_alloc
+ return size_t(1) << (SizeDigits - count + 1);
}
inline constexpr size_t bucketForHash(size_t nBuckets, size_t hash) noexcept
{
@@ -460,6 +457,11 @@ struct Data
size_t seed = 0;
Span *spans = nullptr;
+ static constexpr size_t maxNumBuckets() noexcept
+ {
+ return (std::numeric_limits<ptrdiff_t>::max)() / sizeof(Span);
+ }
+
struct Bucket {
Span *span;
size_t index;
@@ -529,11 +531,29 @@ struct Data
}
};
+ static auto allocateSpans(size_t numBuckets)
+ {
+ struct R {
+ Span *spans;
+ size_t nSpans;
+ };
+
+ constexpr qptrdiff MaxSpanCount = (std::numeric_limits<qptrdiff>::max)() / sizeof(Span);
+ constexpr size_t MaxBucketCount = MaxSpanCount << SpanConstants::SpanShift;
+
+ if (numBuckets > MaxBucketCount) {
+ Q_CHECK_PTR(false);
+ Q_UNREACHABLE(); // no exceptions and no assertions -> no error reporting
+ }
+
+ size_t nSpans = numBuckets >> SpanConstants::SpanShift;
+ return R{ new Span[nSpans], nSpans };
+ }
+
Data(size_t reserve = 0)
{
numBuckets = GrowthPolicy::bucketsForCapacity(reserve);
- size_t nSpans = numBuckets >> SpanConstants::SpanShift;
- spans = new Span[nSpans];
+ spans = allocateSpans(numBuckets).spans;
seed = QHashSeed::globalSeed();
}
@@ -555,17 +575,16 @@ struct Data
Data(const Data &other) : size(other.size), numBuckets(other.numBuckets), seed(other.seed)
{
- size_t nSpans = numBuckets >> SpanConstants::SpanShift;
- spans = new Span[nSpans];
- reallocationHelper(other, nSpans, false);
+ auto r = allocateSpans(numBuckets);
+ spans = r.spans;
+ reallocationHelper(other, r.nSpans, false);
}
Data(const Data &other, size_t reserved) : size(other.size), seed(other.seed)
{
numBuckets = GrowthPolicy::bucketsForCapacity(qMax(size, reserved));
- size_t nSpans = numBuckets >> SpanConstants::SpanShift;
- spans = new Span[nSpans];
+ 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)
@@ -621,8 +640,7 @@ struct Data
Span *oldSpans = spans;
size_t oldBucketCount = numBuckets;
- size_t nSpans = newBucketCount >> SpanConstants::SpanShift;
- spans = new Span[nSpans];
+ spans = allocateSpans(newBucketCount).spans;
numBuckets = newBucketCount;
size_t oldNSpans = oldBucketCount >> SpanConstants::SpanShift;
@@ -659,8 +677,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));
@@ -679,24 +699,12 @@ struct Data
}
}
- Node *findNode(const Key &key) const noexcept
+ template <typename K> Node *findNode(const K &key) const noexcept
{
- Q_ASSERT(numBuckets > 0);
- size_t hash = QHashPrivate::calculateHash(key, seed);
- Bucket bucket(this, GrowthPolicy::bucketForHash(numBuckets, hash));
- // loop over the buckets until we find the entry we search for
- // or an empty slot, in which case we know the entry doesn't exist
- while (true) {
- size_t offset = bucket.offset();
- if (offset == SpanConstants::UnusedEntry) {
- return nullptr;
- } else {
- Node &n = bucket.nodeAtOffset(offset);
- if (qHashEquals(n.key, key))
- return &n;
- }
- bucket.advanceWrapped(this);
- }
+ auto bucket = findBucket(key);
+ if (bucket.isUnused())
+ return nullptr;
+ return bucket.node();
}
struct InsertionResult
@@ -705,7 +713,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) {
@@ -891,7 +899,7 @@ public:
#endif
void swap(QHash &other) noexcept { qt_ptr_swap(d, other.d); }
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename AKey = Key, typename AT = T>
QTypeTraits::compare_eq_result_container<QHash, AKey, AT> operator==(const QHash &other) const noexcept
{
@@ -914,7 +922,7 @@ public:
#else
bool operator==(const QHash &other) const;
bool operator!=(const QHash &other) const;
-#endif // Q_CLANG_QDOC
+#endif // Q_QDOC
inline qsizetype size() const noexcept { return d ? qsizetype(d->size) : 0; }
inline bool isEmpty() const noexcept { return !d || d->size == 0; }
@@ -949,6 +957,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);
@@ -961,13 +974,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);
@@ -982,6 +1003,7 @@ public:
return value;
}
+public:
bool contains(const Key &key) const noexcept
{
if (!d)
@@ -994,74 +1016,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);
@@ -1228,28 +1244,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();
@@ -1261,7 +1274,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();
@@ -1270,6 +1283,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);
@@ -1318,7 +1344,7 @@ public:
float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
static float max_load_factor() noexcept { return 0.5; }
size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
- static size_t max_bucket_count() noexcept { return QHashPrivate::GrowthPolicy::maxNumBuckets(); }
+ static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
inline bool empty() const noexcept { return isEmpty(); }
@@ -1333,8 +1359,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>
@@ -1412,8 +1495,8 @@ public:
return *this;
}
QMultiHash(QMultiHash &&other) noexcept
- : d(qExchange(other.d, nullptr)),
- m_size(qExchange(other.m_size, 0))
+ : d(std::exchange(other.d, nullptr)),
+ m_size(std::exchange(other.m_size, 0))
{
}
QMultiHash &operator=(QMultiHash &&other) noexcept(std::is_nothrow_destructible<Node>::value)
@@ -1438,7 +1521,7 @@ public:
std::swap(m_size, other.m_size);
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename AKey = Key, typename AT = T>
QTypeTraits::compare_eq_result_container<QMultiHash, AKey, AT> operator==(const QMultiHash &other) const noexcept
{
@@ -1479,7 +1562,7 @@ public:
#else
bool operator==(const QMultiHash &other) const;
bool operator!=(const QMultiHash &other) const;
-#endif // Q_CLANG_QDOC
+#endif // Q_QDOC
inline qsizetype size() const noexcept { return m_size; }
@@ -1512,6 +1595,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);
@@ -1527,13 +1615,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);
@@ -1558,6 +1654,7 @@ public:
return t;
}
+public:
bool contains(const Key &key) const noexcept
{
if (!d)
@@ -1566,75 +1663,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());
+ if (!result.initialized) {
+ 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);
@@ -1665,9 +1758,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);
@@ -1682,6 +1781,7 @@ public:
return values;
}
+public:
class const_iterator;
class iterator
@@ -1893,7 +1993,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();
@@ -1906,11 +2008,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();
@@ -1919,6 +2017,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);
@@ -1948,7 +2060,7 @@ public:
float load_factor() const noexcept { return d ? d->loadFactor() : 0; }
static float max_load_factor() noexcept { return 0.5; }
size_t bucket_count() const noexcept { return d ? d->numBuckets : 0; }
- static size_t max_bucket_count() noexcept { return QHashPrivate::GrowthPolicy::maxNumBuckets(); }
+ static size_t max_bucket_count() noexcept { return Data::maxNumBuckets(); }
inline bool empty() const noexcept { return isEmpty(); }
@@ -1984,6 +2096,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);
@@ -1992,8 +2109,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);
@@ -2022,8 +2145,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);
@@ -2039,8 +2168,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);
@@ -2057,7 +2192,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();
@@ -2066,11 +2201,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());
@@ -2082,6 +2213,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()) {
@@ -2117,29 +2263,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 = qAsConst(*this).equal_range(key);
- return qMakePair(iterator(pair.first.i), iterator(pair.second.i));
+ auto pair = std::as_const(*this).equal_range(key);
+ 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) {
@@ -2176,6 +2332,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 04d165983d..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
@@ -68,14 +88,6 @@ Q_DECL_CONST_FUNCTION constexpr size_t hash(size_t key, size_t seed) noexcept
}
}
-template <typename T, typename = void>
-constexpr inline bool HasQHashSingleArgOverload = false;
-
-template <typename T>
-constexpr inline bool HasQHashSingleArgOverload<T, std::enable_if_t<
- std::is_convertible_v<decltype(qHash(std::declval<const T &>())), size_t>
->> = true;
-
template <typename T1, typename T2> static constexpr bool noexceptPairHash();
}
@@ -110,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
@@ -120,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_CLANG_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
@@ -141,17 +183,29 @@ Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(std::nullptr_t, size_t seed
{
return seed;
}
+template <class Enum, std::enable_if_t<std::is_enum_v<Enum>, bool> = true>
+Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(Enum e, size_t seed = 0) noexcept
+{ return QHashPrivate::hash(qToUnderlying(e), seed); }
// (some) Qt types
Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(const QChar key, size_t seed = 0) noexcept { return qHash(key.unicode(), seed); }
+
+#if QT_CORE_REMOVED_SINCE(6, 4)
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(const QByteArray &key, size_t seed = 0) noexcept;
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(const QByteArrayView &key, size_t seed = 0) noexcept;
+#else
+Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(QByteArrayView key, size_t seed = 0) noexcept;
+inline Q_DECL_PURE_FUNCTION size_t qHash(const QByteArray &key, size_t seed = 0
+ QT6_DECL_NEW_OVERLOAD_TAIL) noexcept
+{ return qHash(qToByteArrayViewIgnoringNull(key), seed); }
+#endif
+
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION size_t qHash(QStringView key, size_t seed = 0) noexcept;
-#if QT_STRINGVIEW_LEVEL < 2
inline Q_DECL_PURE_FUNCTION size_t qHash(const QString &key, size_t seed = 0) noexcept
{ return qHash(QStringView{key}, seed); }
-#endif
+#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); }
@@ -161,9 +215,42 @@ template <typename Enum>
Q_DECL_CONST_FUNCTION constexpr inline size_t qHash(QFlags<Enum> flags, size_t seed = 0) noexcept
{ return qHash(flags.toInt(), seed); }
-template <typename T, std::enable_if_t<QHashPrivate::HasQHashSingleArgOverload<T>, bool> = true>
+// ### Qt 7: remove this "catch-all" overload logic, and require users
+// to provide the two-argument version of qHash.
+#if (QT_VERSION < QT_VERSION_CHECK(7, 0, 0))
+// Beware of moving this code from here. It needs to see all the
+// declarations of qHash overloads for C++ fundamental types *before*
+// its own declaration.
+namespace QHashPrivate {
+template <typename T, typename = void>
+constexpr inline bool HasQHashSingleArgOverload = false;
+
+template <typename T>
+constexpr inline bool HasQHashSingleArgOverload<T, std::enable_if_t<
+ std::is_convertible_v<decltype(qHash(std::declval<const T &>())), size_t>
+>> = true;
+}
+
+template <typename T, std::enable_if_t<QHashPrivate::HasQHashSingleArgOverload<T> && !std::is_enum_v<T>, bool> = true>
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)
@@ -171,6 +258,13 @@ 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
@@ -307,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 cff535d030..8a2b493ef4 100644
--- a/src/corelib/tools/qiterator.h
+++ b/src/corelib/tools/qiterator.h
@@ -5,11 +5,19 @@
#define QITERATOR_H
#include <QtCore/qglobal.h>
+#include <QtCore/qcontainertools_impl.h>
QT_BEGIN_NAMESPACE
#if !defined(QT_NO_JAVA_STYLE_ITERATORS)
+#ifdef Q_QDOC
+#define Q_DISABLE_BACKWARD_ITERATOR
+#else
+#define Q_DISABLE_BACKWARD_ITERATOR \
+ template<typename It = decltype(i), QtPrivate::IfIteratorCanMoveBackwards<It> = true>
+#endif
+
#define Q_DECLARE_SEQUENTIAL_ITERATOR(C) \
\
template <class T> \
@@ -28,11 +36,15 @@ public: \
inline bool hasNext() const { return i != c.constEnd(); } \
inline const T &next() { return *i++; } \
inline const T &peekNext() const { return *i; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool hasPrevious() const { return i != c.constBegin(); } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline const T &previous() { return *--i; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline const T &peekPrevious() const { const_iterator p = i; return *--p; } \
inline bool findNext(const T &t) \
{ while (i != c.constEnd()) if (*i++ == t) return true; return false; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool findPrevious(const T &t) \
{ while (i != c.constBegin()) if (*(--i) == t) return true; \
return false; } \
@@ -59,8 +71,11 @@ public: \
inline bool hasNext() const { return c->constEnd() != const_iterator(i); } \
inline T &next() { n = i++; return *n; } \
inline T &peekNext() const { return *i; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool hasPrevious() const { return c->constBegin() != const_iterator(i); } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline T &previous() { n = --i; return *n; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline T &peekPrevious() const { iterator p = i; return *--p; } \
inline void remove() \
{ if (c->constEnd() != const_iterator(n)) { i = c->erase(n); n = c->end(); } } \
@@ -70,6 +85,7 @@ public: \
inline void insert(const T &t) { n = i = c->insert(i, t); ++i; } \
inline bool findNext(const T &t) \
{ while (c->constEnd() != const_iterator(n = i)) if (*i++ == t) return true; return false; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool findPrevious(const T &t) \
{ while (c->constBegin() != const_iterator(i)) if (*(n = --i) == t) return true; \
n = c->end(); return false; } \
@@ -95,13 +111,17 @@ public: \
inline bool hasNext() const { return i != c.constEnd(); } \
inline Item next() { n = i++; return n; } \
inline Item peekNext() const { return i; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool hasPrevious() const { return i != c.constBegin(); } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline Item previous() { n = --i; return n; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline Item peekPrevious() const { const_iterator p = i; return --p; } \
inline const T &value() const { Q_ASSERT(item_exists()); return *n; } \
inline const Key &key() const { Q_ASSERT(item_exists()); return n.key(); } \
inline bool findNext(const T &t) \
{ while ((n = i) != c.constEnd()) if (*i++ == t) return true; return false; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool findPrevious(const T &t) \
{ while (i != c.constBegin()) if (*(n = --i) == t) return true; \
n = c.constEnd(); return false; } \
@@ -129,8 +149,11 @@ public: \
inline bool hasNext() const { return const_iterator(i) != c->constEnd(); } \
inline Item next() { n = i++; return n; } \
inline Item peekNext() const { return i; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool hasPrevious() const { return const_iterator(i) != c->constBegin(); } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline Item previous() { n = --i; return n; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline Item peekPrevious() const { iterator p = i; return --p; } \
inline void remove() \
{ if (const_iterator(n) != c->constEnd()) { i = c->erase(n); n = c->end(); } } \
@@ -140,6 +163,7 @@ public: \
inline const Key &key() const { Q_ASSERT(item_exists()); return n.key(); } \
inline bool findNext(const T &t) \
{ while (const_iterator(n = i) != c->constEnd()) if (*i++ == t) return true; return false; } \
+ Q_DISABLE_BACKWARD_ITERATOR \
inline bool findPrevious(const T &t) \
{ while (const_iterator(i) != c->constBegin()) if (*(n = --i) == t) return true; \
n = c->end(); return false; } \
@@ -230,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 f4286c99ea..041fb0701d 100644
--- a/src/corelib/tools/qiterator.qdoc
+++ b/src/corelib/tools/qiterator.qdoc
@@ -84,7 +84,7 @@
/*!
\fn template<typename Key, typename T, class Iterator> QKeyValueIterator &QKeyValueIterator<Key, T, Iterator>::operator++()
- The prefix ++ operator (\c{++i}) advances the iterator to the
+ The prefix \c{++} operator (\c{++i}) advances the iterator to the
next item in the container and returns the iterator.
\note Advancing the iterator past its container's end() constitutes
@@ -97,7 +97,7 @@
\overload
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the container and returns the iterator's prior value.
\note Advancing the iterator past its container's end() constitutes
@@ -106,7 +106,7 @@
/*! \fn template<typename Key, typename T, class Iterator> QKeyValueIterator &QKeyValueIterator<Key, T, Iterator>::operator--()
- The prefix -- operator (\c{--i}) backs the iterator up to the previous item
+ The prefix c{--} operator (\c{--i}) backs the iterator up to the previous item
in the container and returns the iterator.
\note Backing up an iterator to before its container's begin() constitutes
@@ -119,7 +119,7 @@
\overload
- The postfix -- operator (\c{i--}) backs the iterator up to the previous item
+ The postfix c{--} operator (\c{i--}) backs the iterator up to the previous item
in the container and returns the iterator's prior value.
\note Backing up an iterator to before its container's begin() constitutes
@@ -213,12 +213,8 @@
\image javaiterators1.png
- Here's how to iterate over the elements in reverse order:
-
- \snippet code/doc_src_qiterator.cpp 7
-
If you want to find all occurrences of a particular value, use
- findNext() or findPrevious() in a loop.
+ findNext() in a loop.
Multiple iterators can be used on the same set. If the set
is modified while a QSetIterator is active, the QSetIterator
@@ -389,15 +385,21 @@
*/
/*! \fn template <class T> void QListIterator<T>::toBack()
- \fn template <class T> void QSetIterator<T>::toBack()
\fn template <class T> void QMutableListIterator<T>::toBack()
+//! [toBack]
Moves the iterator to the back of the container (after the last
item).
+//! [toBack]
\sa toFront(), previous()
*/
+/*! \fn template <class T> void QSetIterator<T>::toBack()
+ \include qiterator.qdoc toBack
+ \sa toFront()
+*/
+
/*! \fn template <class T> void QMutableSetIterator<T>::toBack()
Moves the iterator to the back of the container (after the last
@@ -407,16 +409,22 @@
*/
/*! \fn template <class T> bool QListIterator<T>::hasNext() const
- \fn template <class T> bool QSetIterator<T>::hasNext() const
\fn template <class T> bool QMutableListIterator<T>::hasNext() const
+//! [hasNext]
Returns \c true if there is at least one item ahead of the iterator,
i.e. the iterator is \e not at the back of the container;
otherwise returns \c false.
+//! [hasNext]
\sa hasPrevious(), next()
*/
+/*! \fn template <class T> bool QSetIterator<T>::hasNext() const
+ \include qiterator.qdoc hasNext
+ \sa next()
+*/
+
/*! \fn template <class T> bool QMutableSetIterator<T>::hasNext() const
Returns \c true if there is at least one item ahead of the iterator,
@@ -427,16 +435,23 @@
*/
/*! \fn template <class T> const T &QListIterator<T>::next()
- \fn template <class T> const T &QSetIterator<T>::next()
+//! [next]
Returns the next item and advances the iterator by one position.
Calling this function on an iterator located at the back of the
container leads to undefined results.
+//! [next]
\sa hasNext(), peekNext(), previous()
*/
+/*!
+ \fn template <class T> const T &QSetIterator<T>::next()
+ \include qiterator.qdoc next
+ \sa hasNext(), peekNext()
+*/
+
/* \fn template <class T> const T &QMutableSetIterator<T>::next()
Returns the next item and advances the iterator by one position.
@@ -468,17 +483,24 @@
*/
/*! \fn template <class T> const T &QListIterator<T>::peekNext() const
- \fn template <class T> const T &QSetIterator<T>::peekNext() const
+//! [peekNext]
Returns the next item without moving the iterator.
Calling this function on an iterator located at the back of the
container leads to undefined results.
+//! [peekNext]
\sa hasNext(), next(), peekPrevious()
*/
/*!
+ \fn template <class T> const T &QSetIterator<T>::peekNext() const
+ \include qiterator.qdoc peekNext
+ \sa hasNext(), next()
+*/
+
+/*!
\fn template <class T> const T &QMutableSetIterator<T>::peekNext() const
Returns the next item without moving the iterator.
@@ -500,7 +522,6 @@
*/
/*! \fn template <class T> bool QListIterator<T>::hasPrevious() const
- \fn template <class T> bool QSetIterator<T>::hasPrevious() const
\fn template <class T> bool QMutableListIterator<T>::hasPrevious() const
Returns \c true if there is at least one item behind the iterator,
@@ -511,7 +532,6 @@
*/
/*! \fn template <class T> const T &QListIterator<T>::previous()
- \fn template <class T> const T &QSetIterator<T>::previous()
Returns the previous item and moves the iterator back by one
position.
@@ -534,7 +554,6 @@
*/
/*! \fn template <class T> const T &QListIterator<T>::peekPrevious() const
- \fn template <class T> const T &QSetIterator<T>::peekPrevious() const
Returns the previous item without moving the iterator.
@@ -566,21 +585,25 @@
*/
/*! \fn template <class T> bool QListIterator<T>::findNext(const T &value)
- \fn template <class T> bool QSetIterator<T>::findNext(const T &value)
\fn template <class T> bool QMutableListIterator<T>::findNext(const T &value)
+//! [findNext]
Searches for \a value starting from the current iterator position
forward. Returns \c true if \a value is found; otherwise returns \c false.
After the call, if \a value was found, the iterator is positioned
just after the matching item; otherwise, the iterator is
positioned at the back of the container.
+//! [findNext]
\sa findPrevious()
*/
+/*! \fn template <class T> bool QSetIterator<T>::findNext(const T &value)
+ \include qiterator.qdoc findNext
+*/
+
/*! \fn template <class T> bool QListIterator<T>::findPrevious(const T &value)
- \fn template <class T> bool QSetIterator<T>::findPrevious(const T &value)
\fn template <class T> bool QMutableListIterator<T>::findPrevious(const T &value)
Searches for \a value starting from the current iterator position
@@ -950,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.h b/src/corelib/tools/qline.h
index 5961d5a57e..e23ffbe9d5 100644
--- a/src/corelib/tools/qline.h
+++ b/src/corelib/tools/qline.h
@@ -56,7 +56,7 @@ public:
private:
QPoint pt1, pt2;
};
-Q_DECLARE_TYPEINFO(QLine, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QLine, Q_PRIMITIVE_TYPE);
/*******************************************************************************
* class QLine inline members
@@ -241,7 +241,7 @@ public:
private:
QPointF pt1, pt2;
};
-Q_DECLARE_TYPEINFO(QLineF, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(QLineF, Q_PRIMITIVE_TYPE);
/*******************************************************************************
* class QLineF inline members
diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h
index 0cc62e5f6e..89e0e3f380 100644
--- a/src/corelib/tools/qlist.h
+++ b/src/corelib/tools/qlist.h
@@ -10,12 +10,15 @@
#include <QtCore/qhashfunctions.h>
#include <QtCore/qiterator.h>
#include <QtCore/qcontainertools_impl.h>
+#include <QtCore/qnamespace.h>
#include <functional>
#include <limits>
#include <initializer_list>
#include <type_traits>
+class tst_QList;
+
QT_BEGIN_NAMESPACE
namespace QtPrivate {
@@ -75,10 +78,15 @@ class QList
using DataPointer = QArrayDataPointer<T>;
class DisableRValueRefs {};
+ friend class ::tst_QList;
+
DataPointer d;
template <typename V, typename U> friend qsizetype QtPrivate::indexOf(const QList<V> &list, const U &u, qsizetype from) noexcept;
template <typename V, typename U> friend qsizetype QtPrivate::lastIndexOf(const QList<V> &list, const U &u, qsizetype from) noexcept;
+ // This alias prevents the QtPrivate namespace from being exposed into the docs.
+ template <typename InputIterator>
+ using if_input_iterator = QtPrivate::IfIsInputIterator<InputIterator>;
public:
using Type = T;
@@ -109,8 +117,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
@@ -180,8 +187,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
@@ -252,6 +258,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)
@@ -261,20 +275,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(args.size()))
+ : d(qsizetype(args.size()))
{
if (args.size())
d->copyAppend(args.begin(), args.end());
@@ -282,12 +296,10 @@ public:
QList<T> &operator=(std::initializer_list<T> args)
{
- d = DataPointer(Data::allocate(args.size()));
- if (args.size())
- d->copyAppend(args.begin(), args.end());
- return *this;
+ return assign(args);
}
- template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
+
+ template <typename InputIterator, if_input_iterator<InputIterator> = true>
QList(InputIterator i1, InputIterator i2)
{
if constexpr (!std::is_convertible_v<typename std::iterator_traits<InputIterator>::iterator_category, std::forward_iterator_tag>) {
@@ -295,7 +307,7 @@ public:
} else {
const auto distance = std::distance(i1, i2);
if (distance) {
- d = DataPointer(Data::allocate(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> ||
@@ -313,11 +325,18 @@ 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); }
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename U = T>
QTypeTraits::compare_eq_result_container<QList, U> operator==(const QList &other) const
{
@@ -373,7 +392,7 @@ public:
bool operator>(const QList &other) const;
bool operator<=(const QList &other) const;
bool operator>=(const QList &other) const;
-#endif // Q_CLANG_QDOC
+#endif // Q_QDOC
qsizetype size() const noexcept { return d->size; }
qsizetype count() const noexcept { return size(); }
@@ -393,6 +412,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);
@@ -411,7 +436,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);
@@ -426,7 +451,7 @@ public:
reference operator[](qsizetype i)
{
Q_ASSERT_X(size_t(i) < size_t(d->size), "QList::operator[]", "index out of range");
- detach();
+ // don't detach() here, we detach in data below:
return data()[i];
}
const_reference operator[](qsizetype i) const noexcept { return at(i); }
@@ -488,6 +513,19 @@ public:
}
}
+ QList &assign(qsizetype n, parameter_type t)
+ {
+ Q_ASSERT(n >= 0);
+ return fill(t, n);
+ }
+
+ template <typename InputIterator, if_input_iterator<InputIterator> = true>
+ QList &assign(InputIterator first, InputIterator last)
+ { d.assign(first, last); return *this; }
+
+ QList &assign(std::initializer_list<T> l)
+ { return assign(l.begin(), l.end()); }
+
template <typename ...Args>
iterator emplace(const_iterator before, Args&&... args)
{
@@ -615,27 +653,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;
@@ -665,14 +689,22 @@ 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; }
QList<T> &operator+=(QList<T> &&l) { append(std::move(l)); return *this; }
- inline QList<T> operator+(const QList<T> &l) const
+ inline QList<T> operator+(const QList<T> &l) const &
{ QList n = *this; n += l; return n; }
- inline QList<T> operator+(QList<T> &&l) const
+ QList<T> operator+(const QList<T> &l) &&
+ { return std::move(*this += l); }
+ inline QList<T> operator+(QList<T> &&l) const &
{ QList n = *this; n += std::move(l); return n; }
+ QList<T> operator+(QList<T> &&l) &&
+ { return std::move(*this += std::move(l)); }
inline QList<T> &operator+=(parameter_type t)
{ append(t); return *this; }
inline QList<T> &operator<< (parameter_type t)
@@ -731,7 +763,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);
@@ -745,7 +777,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);
@@ -874,7 +906,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 {
@@ -956,7 +988,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 e8e5a73596..e07b74cd53 100644
--- a/src/corelib/tools/qlist.qdoc
+++ b/src/corelib/tools/qlist.qdoc
@@ -129,21 +129,7 @@
support \c operator==(). These requirements are documented on a
per-function basis.
- Like the other container classes, QList provides \l{Java-style iterators}
- (QListIterator and QMutableListIterator) and \l{STL-style iterators}
- (QList::const_iterator and QList::iterator). In practice, iterators are
- handy when working with generic algorithms provided by \l{generic
- algorithms}{Qt} and the C++ standard library. \l{Java-style iterators} are
- provided for backwards compatibility, prefer \l{STL-style iterators} when
- writing C++ code.
-
- \note Iterators over a QList, and references to individual elements
- within one, cannot be relied on to remain valid when any non-const method
- of the QList is called. Accessing such an iterator or reference after
- the call to a non-const method leads to undefined behavior. When stability
- for iterator-like functionality is required, you should use indexes instead
- of iterators as they are not tied to QList's internal state and thus do
- not get invalidated.
+ For iterating over the items, see \l {Iterating over Containers}.
In addition to QList, Qt also provides QVarLengthArray, a very
low-level class with little functionality that is optimized for
@@ -261,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.
@@ -288,11 +299,15 @@
Constructs a list from the std::initializer_list given by \a args.
*/
-/*! \fn template <typename T> template<typename 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).
+ \note This constructor only participates in overload resolution if
+ \c InputIterator meets the requirements of a
+ \l {https://en.cppreference.com/w/cpp/named_req/InputIterator} {LegacyInputIterator}.
+
The value type of \c InputIterator must be convertible to \c T.
*/
@@ -436,18 +451,35 @@
*/
/*! \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.
- Since Qt 5.6, resize() doesn't shrink the capacity anymore.
- To shed excess capacity, use squeeze().
+ If this list is not shared, the capacity() is preserved. Use squeeze()
+ to shed excess capacity.
+
+ \note In Qt versions prior to 5.7 (for QVector; QList lacked a resize()
+ until 6.0), this function released the memory used by the list instead of
+ preserving the capacity.
\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
@@ -563,17 +595,14 @@
Removes all the elements from the list.
- \note Until Qt 5.6, this also released the memory used by
- the list. From Qt 5.7, the capacity is preserved. To shed
- all capacity, swap with a default-constructed list:
- \code
- QList<T> l ...;
- QList<T>().swap(l);
- Q_ASSERT(l.capacity() == 0);
- \endcode
- or call squeeze().
+ If this list is not shared, the capacity() is preserved. Use squeeze() to
+ shed excess capacity.
+
+ \note In Qt versions prior to 5.7 (for QVector) and 6.0 (for QList), this
+ function released the memory used by the list instead of preserving the
+ capacity.
- \sa squeeze()
+ \sa resize(), squeeze()
*/
/*! \fn template <typename T> const T &QList<T>::at(qsizetype i) const
@@ -1300,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
@@ -1333,7 +1371,11 @@
\sa append(), operator<<()
*/
-/*! \fn template <typename T> QList<T> QList<T>::operator+(const QList<T> &other) const
+/*!
+ \fn template <typename T> QList<T> QList<T>::operator+(const QList<T> &other) const &
+ \fn template <typename T> QList<T> QList<T>::operator+(const QList<T> &other) &&
+ \fn template <typename T> QList<T> QList<T>::operator+(QList<T> &&other) const &
+ \fn template <typename T> QList<T> QList<T>::operator+(QList<T> &&other) &&
Returns a list that contains all the items in this list
followed by all the items in the \a other list.
@@ -1341,14 +1383,6 @@
\sa operator+=()
*/
-/*! \fn template <typename T> QList<T> QList<T>::operator+(QList<T> &&other) const
- \since 6.0
-
- \overload
-
- \sa operator+=()
-*/
-
/*! \fn template <typename T> QList<T> &QList<T>::operator<<(parameter_type value)
Appends \a value to the list and returns a reference to this list.
@@ -1549,3 +1583,47 @@
\sa erase
*/
+
+/*! \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.
+
+ The size of this list will be equal to \a n.
+
+ This function will only allocate memory if \a n exceeds the capacity of the
+ list or this list is shared.
+*/
+
+/*! \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
+ iterator range [\a first, \a last).
+
+ The size of this list will be equal to the number of elements in the
+ range [\a first, \a last).
+
+ This function will only allocate memory if the number of elements in the
+ range exceeds the capacity of this list or this list is shared.
+
+ \note This function overload only participates in overload resolution if
+ \c InputIterator meets the requirements of a
+ \l {https://en.cppreference.com/w/cpp/named_req/InputIterator} {LegacyInputIterator}.
+
+ \note The behavior is undefined if either argument is an iterator into
+ *this.
+*/
+
+/*! \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
+ \a l.
+
+ The size of this list will be equal to the number of elements in
+ \a l.
+
+ This function only allocates memory if the number of elements in \a l
+ exceeds the capacity of this list or this list is shared.
+*/
diff --git a/src/corelib/tools/qmap.h b/src/corelib/tools/qmap.h
index 9ba01bee2c..7ee0be1e51 100644
--- a/src/corelib/tools/qmap.h
+++ b/src/corelib/tools/qmap.h
@@ -5,6 +5,7 @@
#ifndef QMAP_H
#define QMAP_H
+#include <QtCore/qhashfunctions.h>
#include <QtCore/qiterator.h>
#include <QtCore/qlist.h>
#include <QtCore/qrefcount.h>
@@ -240,7 +241,7 @@ public:
return {};
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename AKey = Key, typename AT = T> friend
QTypeTraits::compare_eq_result_container<QMap, AKey, AT> operator==(const QMap &lhs, const QMap &rhs)
{
@@ -261,7 +262,7 @@ public:
#else
friend bool operator==(const QMap &lhs, const QMap &rhs);
friend bool operator!=(const QMap &lhs, const QMap &rhs);
-#endif // Q_CLANG_QDOC
+#endif // Q_QDOC
size_type size() const { return d ? size_type(d->m.size()) : size_type(0); }
@@ -762,7 +763,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 +771,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 +814,7 @@ qsizetype erase_if(QMap<Key, T> &map, Predicate pred)
return QtPrivate::associative_erase_if(map, pred);
}
+
//
// QMultiMap
//
@@ -885,7 +912,7 @@ public:
return {};
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename AKey = Key, typename AT = T> friend
QTypeTraits::compare_eq_result_container<QMultiMap, AKey, AT> operator==(const QMultiMap &lhs, const QMultiMap &rhs)
{
@@ -906,7 +933,7 @@ public:
#else
friend bool operator==(const QMultiMap &lhs, const QMultiMap &rhs);
friend bool operator!=(const QMultiMap &lhs, const QMultiMap &rhs);
-#endif // Q_CLANG_QDOC
+#endif // Q_QDOC
size_type size() const { return d ? size_type(d->m.size()) : size_type(0); }
@@ -1492,7 +1519,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 +1527,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 5d0825708b..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
@@ -87,27 +87,17 @@
The items are traversed in ascending key order.
- Normally, a QMap allows only one value per key. If you call
+ A QMap allows only one value per key. If you call
insert() with a key that already exists in the QMap, the
previous value will be erased. For example:
\snippet code/src_corelib_tools_qmap.cpp 9
However, you can store multiple values per key by using
- QMultiMap. If you want
- to retrieve all the values for a single key, you can use
- values(const Key &key), which returns a QList<T>:
-
- \snippet code/src_corelib_tools_qmap.cpp 10
-
- Another approach is to call
- find() to get the STL-style iterator for the first item with a
- key and iterate from there:
-
- \snippet code/src_corelib_tools_qmap.cpp 11
+ QMultiMap.
If you only need to extract the values from a map (not the keys),
- you can also use \l{foreach}:
+ you can also use range-based for:
\snippet code/src_corelib_tools_qmap.cpp 12
@@ -212,6 +202,15 @@
Returns an STL map equivalent to this QMap.
*/
+/*! \fn template <class Key, class T> std::map<Key, T> QMap<Key, T>::toStdMap() &&
+
+ \overload
+ \since 6.0
+
+ \note Calling this function will leave this QMap in the partially-formed state, in which
+ the only valid operations are destruction or assignment of a new value.
+*/
+
/*! \fn template <class Key, class T> bool QMap<Key, T>::operator==(const QMap<Key, T> &lhs, const QMap<Key, T> &rhs)
Returns \c true if \a lhs is equal to \a rhs; otherwise returns
@@ -318,9 +317,7 @@
the value associated with it.
If the item does not exist in the map, the function simply
- returns a \l{default-constructed value}. If there are multiple
- items for \a key in the map, only the most recently inserted one
- is removed and returned.
+ returns a \l{default-constructed value}.
If you don't use the return value, remove() is more efficient.
@@ -370,9 +367,7 @@
If the map contains no item with key \a key, the function inserts
a \l{default-constructed value} into the map with key \a key, and
- returns a reference to it. If the map contains multiple items
- with key \a key, this function returns a reference to the most
- recently inserted value.
+ returns a reference to it.
\sa insert(), value()
*/
@@ -395,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
@@ -413,9 +408,7 @@
/*! \fn template <class Key, class T> QList<T> QMap<Key, T>::values() const
Returns a list containing all the values in the map, in ascending
- order of their keys. If a key is associated with multiple values,
- all of its values will be in the list, and not just the most
- recently inserted one.
+ order of their keys.
This function creates a new list, in \l {linear time}. The time and memory
use that entails can be avoided by iterating from \l keyValueBegin() to
@@ -737,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)
@@ -763,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)
@@ -773,11 +766,6 @@
If a key is common to both maps, its value will be replaced with
the value stored in \a map.
-
- \note If \a map contains multiple entries with the same key then the
- final value of the key is undefined.
-
- \sa QMultiMap::insert()
*/
/*! \fn template <class Key, class T> void QMap<Key, T>::insert(QMap<Key, T> &&map)
@@ -793,12 +781,12 @@
/*! \typedef QMap::Iterator
- Qt-style synonym for QMap<Key, T>::iterator.
+ Qt-style synonym for QMap::iterator.
*/
/*! \typedef QMap::ConstIterator
- Qt-style synonym for QMap<Key, T>::const_iterator.
+ Qt-style synonym for QMap::const_iterator.
*/
/*! \typedef QMap::difference_type
@@ -830,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
*/
@@ -847,12 +835,6 @@
\inmodule QtCore
\brief The QMap::iterator class provides an STL-style non-const iterator for QMap.
- QMap features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QMap\<Key, T\>::iterator allows you to iterate over a QMap
and to modify the value (but not the key) stored under
a particular key. If you want to iterate over a const QMap, you
@@ -872,31 +854,15 @@
Unlike QHash, which stores its items in an arbitrary order, QMap
stores its items ordered by key.
- Let's see a few examples of things we can do with a
- QMap::iterator that we cannot do with a QMap::const_iterator.
Here's an example that increments every value stored in the QMap
by 2:
\snippet code/src_corelib_tools_qmap.cpp 19
- Here's an example that removes all the items whose key is a
- string that starts with an underscore character:
-
- \snippet code/src_corelib_tools_qmap.cpp 20
-
- The call to QMap::erase() removes the item pointed to by the
- iterator from the map, and returns an iterator to the next item.
- Here's another way of removing an item while iterating:
+ To remove elements from a QMap you can use erase_if(QMap\<Key, T\> &map, Predicate pred):
\snippet code/src_corelib_tools_qmap.cpp 21
- It might be tempting to write code like this:
-
- \snippet code/src_corelib_tools_qmap.cpp 22
-
- However, this will potentially crash in \c{++i}, because \c i is
- a dangling iterator after the call to erase().
-
Multiple iterators can be used on the same map. If you add items
to the map, existing iterators will remain valid. If you remove
items from the map, iterators that point to the removed items
@@ -907,7 +873,7 @@
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QMap::const_iterator, QMap::key_iterator, QMutableMapIterator
+ \sa QMap::const_iterator, QMap::key_iterator, QMap::key_value_iterator
*/
/*! \typedef QMap::iterator::difference_type
@@ -1008,7 +974,7 @@
/*! \fn template <class Key, class T> QMap<Key, T>::iterator &QMap<Key, T>::iterator::operator++()
- The prefix ++ operator (\c{++i}) advances the iterator to the
+ The prefix \c{++} operator (\c{++i}) advances the iterator to the
next item in the map and returns an iterator to the new current
item.
@@ -1021,14 +987,14 @@
\overload
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the map and returns an iterator to the previously
current item.
*/
/*! \fn template <class Key, class T> QMap<Key, T>::iterator &QMap<Key, T>::iterator::operator--()
- The prefix -- operator (\c{--i}) makes the preceding item
+ The prefix \c{--} operator (\c{--i}) makes the preceding item
current and returns an iterator pointing to the new current item.
Calling this function on QMap::begin() leads to undefined
@@ -1041,7 +1007,7 @@
\overload
- The postfix -- operator (\c{i--}) makes the preceding item
+ The postfix \c{--} operator (\c{i--}) makes the preceding item
current and returns an iterator pointing to the previously
current item.
*/
@@ -1067,12 +1033,6 @@
\inmodule QtCore
\brief The QMap::const_iterator class provides an STL-style const iterator for QMap.
- QMap features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QMap\<Key, T\>::const_iterator allows you to iterate over a QMap.
If you want to modify the QMap as you iterate
over it, you must use QMap::iterator instead. It is generally
@@ -1083,12 +1043,20 @@
The default QMap::const_iterator constructor creates an
uninitialized iterator. You must initialize it using a QMap
- function like QMap::constBegin(), QMap::constEnd(), or
- QMap::find() before you can start iterating. Here's a typical
+ function like QMap::cbegin(), QMap::cend(), or
+ QMap::constFind() before you can start iterating. Here's a typical
loop that prints all the (key, value) pairs stored in a map:
\snippet code/src_corelib_tools_qmap.cpp 24
+ Here's an example that removes all the items whose value is greater than 10:
+
+ \snippet code/src_corelib_tools_qmap.cpp 20
+
+ And here the same behavior with erase_if()
+
+ \snippet code/src_corelib_tools_qmap.cpp 21
+
Unlike QHash, which stores its items in an arbitrary order, QMap
stores its items ordered by key.
@@ -1102,7 +1070,7 @@
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QMap::iterator, QMap::key_iterator, QMapIterator
+ \sa QMap::iterator, QMap::key_iterator, QMap::const_key_value_iterator
*/
/*! \typedef QMap::const_iterator::difference_type
@@ -1179,7 +1147,7 @@
/*! \fn template <class Key, class T> QMap<Key, T>::const_iterator &QMap<Key, T>::const_iterator::operator++()
- The prefix ++ operator (\c{++i}) advances the iterator to the
+ The prefix \c{++} operator (\c{++i}) advances the iterator to the
next item in the map and returns an iterator to the new current
item.
@@ -1192,14 +1160,14 @@
\overload
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the map and returns an iterator to the previously
current item.
*/
/*! \fn template <class Key, class T> QMap<Key, T>::const_iterator &QMap<Key, T>::const_iterator::operator--()
- The prefix -- operator (\c{--i}) makes the preceding item
+ The prefix \c{--} operator (\c{--i}) makes the preceding item
current and returns an iterator pointing to the new current item.
Calling this function on QMap::begin() leads to undefined
@@ -1212,7 +1180,7 @@
\overload
- The postfix -- operator (\c{i--}) makes the preceding item
+ The postfix \c{--} operator (\c{i--}) makes the preceding item
current and returns an iterator pointing to the previously
current item.
*/
@@ -1320,7 +1288,7 @@
/*!
\fn template <class Key, class T> QMap<Key, T>::key_iterator &QMap<Key, T>::key_iterator::operator++()
- The prefix ++ operator (\c{++i}) advances the iterator to the
+ The prefix \c{++} operator (\c{++i}) advances the iterator to the
next item in the hash and returns an iterator to the new current
item.
@@ -1333,14 +1301,14 @@
\overload
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the hash and returns an iterator to the previous
item.
*/
/*! \fn template <class Key, class T> QMap<Key, T>::key_iterator &QMap<Key, T>::key_iterator::operator--()
- The prefix -- operator (\c{--i}) makes the preceding item
+ The prefix \c{--} operator (\c{--i}) makes the preceding item
current and returns an iterator pointing to the new current item.
Calling this function on QMap::keyBegin() leads to undefined
@@ -1353,7 +1321,7 @@
\overload
- The postfix -- operator (\c{i--}) makes the preceding item
+ The postfix \c{--} operator (\c{i--}) makes the preceding item
current and returns an iterator pointing to the previous
item.
*/
@@ -1421,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 3cc17b1824..1d2cb7d6e5 100644
--- a/src/corelib/tools/qmargins.cpp
+++ b/src/corelib/tools/qmargins.cpp
@@ -484,7 +484,7 @@ QDebug operator<<(QDebug dbg, const QMargins &m)
Returns \c true if all margins are very close to 0; otherwise returns
false.
- \sa {<QtGlobal>::}{qFuzzyIsNull}
+ \sa {<QtNumeric>::}{qFuzzyIsNull()}
*/
diff --git a/src/corelib/tools/qmargins.h b/src/corelib/tools/qmargins.h
index 2d494f4417..f8d7150dfd 100644
--- a/src/corelib/tools/qmargins.h
+++ b/src/corelib/tools/qmargins.h
@@ -6,8 +6,13 @@
#include <QtCore/qnamespace.h>
+#include <QtCore/q20type_traits.h>
+#include <QtCore/q23utility.h>
+
QT_BEGIN_NAMESPACE
+QT_ENABLE_P0846_SEMANTICS_FOR(get)
+
class QMarginsF;
/*****************************************************************************
@@ -66,17 +71,17 @@ private:
template <std::size_t I,
typename M,
std::enable_if_t<(I < 4), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<M>, QMargins>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<M>, QMargins>, bool> = true>
friend constexpr decltype(auto) get(M &&m) noexcept
{
if constexpr (I == 0)
- return (std::forward<M>(m).m_left);
+ return q23::forward_like<M>(m.m_left);
else if constexpr (I == 1)
- return (std::forward<M>(m).m_top);
+ return q23::forward_like<M>(m.m_top);
else if constexpr (I == 2)
- return (std::forward<M>(m).m_right);
+ return q23::forward_like<M>(m.m_right);
else if constexpr (I == 3)
- return (std::forward<M>(m).m_bottom);
+ return q23::forward_like<M>(m.m_bottom);
}
};
@@ -315,17 +320,17 @@ private:
template <std::size_t I,
typename M,
std::enable_if_t<(I < 4), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<M>, QMarginsF>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<M>, QMarginsF>, bool> = true>
friend constexpr decltype(auto) get(M &&m) noexcept
{
if constexpr (I == 0)
- return (std::forward<M>(m).m_left);
+ return q23::forward_like<M>(m.m_left);
else if constexpr (I == 1)
- return (std::forward<M>(m).m_top);
+ return q23::forward_like<M>(m.m_top);
else if constexpr (I == 2)
- return (std::forward<M>(m).m_right);
+ return q23::forward_like<M>(m.m_right);
else if constexpr (I == 3)
- return (std::forward<M>(m).m_bottom);
+ return q23::forward_like<M>(m.m_bottom);
}
};
diff --git a/src/corelib/tools/qmessageauthenticationcode.cpp b/src/corelib/tools/qmessageauthenticationcode.cpp
deleted file mode 100644
index 4ff48fc822..0000000000
--- a/src/corelib/tools/qmessageauthenticationcode.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright (C) 2013 Ruslan Nigmatullin <euroelessar@yandex.ru>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qmessageauthenticationcode.h"
-#include "qvarlengtharray.h"
-
-#include "qtcore-config_p.h"
-
-// Header from rfc6234
-#include "../../3rdparty/rfc6234/sha.h"
-
-#if QT_CONFIG(system_libb2)
-#include <blake2.h>
-#else
-#include "../../3rdparty/blake2/src/blake2.h"
-#endif
-
-QT_BEGIN_NAMESPACE
-
-static int qt_hash_block_size(QCryptographicHash::Algorithm method)
-{
- switch (method) {
- case QCryptographicHash::Md4:
- return 64;
- case QCryptographicHash::Md5:
- return 64;
- case QCryptographicHash::Sha1:
- return SHA1_Message_Block_Size;
- case QCryptographicHash::Sha224:
- return SHA224_Message_Block_Size;
- case QCryptographicHash::Sha256:
- return SHA256_Message_Block_Size;
- case QCryptographicHash::Sha384:
- return SHA384_Message_Block_Size;
- case QCryptographicHash::Sha512:
- return SHA512_Message_Block_Size;
- case QCryptographicHash::RealSha3_224:
- case QCryptographicHash::Keccak_224:
- return 144;
- case QCryptographicHash::RealSha3_256:
- case QCryptographicHash::Keccak_256:
- return 136;
- case QCryptographicHash::RealSha3_384:
- case QCryptographicHash::Keccak_384:
- return 104;
- case QCryptographicHash::RealSha3_512:
- case QCryptographicHash::Keccak_512:
- return 72;
- case QCryptographicHash::Blake2b_160:
- case QCryptographicHash::Blake2b_256:
- case QCryptographicHash::Blake2b_384:
- case QCryptographicHash::Blake2b_512:
- return BLAKE2B_BLOCKBYTES;
- case QCryptographicHash::Blake2s_128:
- case QCryptographicHash::Blake2s_160:
- case QCryptographicHash::Blake2s_224:
- case QCryptographicHash::Blake2s_256:
- return BLAKE2S_BLOCKBYTES;
- }
- return 0;
-}
-
-class QMessageAuthenticationCodePrivate
-{
-public:
- QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m)
- : messageHash(m), method(m), messageHashInited(false)
- {
- }
-
- QByteArray key;
- QByteArray result;
- QCryptographicHash messageHash;
- QCryptographicHash::Algorithm method;
- bool messageHashInited;
-
- void initMessageHash();
-};
-
-void QMessageAuthenticationCodePrivate::initMessageHash()
-{
- if (messageHashInited)
- return;
- messageHashInited = true;
-
- const int blockSize = qt_hash_block_size(method);
-
- if (key.size() > blockSize) {
- key = QCryptographicHash::hash(key, method);
- }
-
- if (key.size() < blockSize) {
- const int size = key.size();
- key.resize(blockSize);
- memset(key.data() + size, 0, blockSize - size);
- }
-
- QVarLengthArray<char> iKeyPad(blockSize);
- const char * const keyData = key.constData();
-
- for (int i = 0; i < blockSize; ++i)
- iKeyPad[i] = keyData[i] ^ 0x36;
-
- messageHash.addData(iKeyPad);
-}
-
-/*!
- \class QMessageAuthenticationCode
- \inmodule QtCore
-
- \brief The QMessageAuthenticationCode class provides a way to generate
- hash-based message authentication codes.
-
- \since 5.1
-
- \ingroup tools
- \reentrant
-
- QMessageAuthenticationCode supports all cryptographic hashes which are supported by
- QCryptographicHash.
-
- 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.
- \snippet qmessageauthenticationcode/main.cpp 2
-
- \sa QCryptographicHash
-*/
-
-/*!
- Constructs an object that can be used to create a cryptographic hash from data
- using method \a method and key \a key.
-*/
-QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method,
- const QByteArray &key)
- : d(new QMessageAuthenticationCodePrivate(method))
-{
- d->key = key;
-}
-
-/*!
- Destroys the object.
-*/
-QMessageAuthenticationCode::~QMessageAuthenticationCode()
-{
- delete d;
-}
-
-/*!
- Resets message data. Calling this method doesn't affect the key.
-*/
-void QMessageAuthenticationCode::reset()
-{
- d->result.clear();
- d->messageHash.reset();
- d->messageHashInited = false;
-}
-
-/*!
- Sets secret \a key. Calling this method automatically resets the object state.
-*/
-void QMessageAuthenticationCode::setKey(const QByteArray &key)
-{
- reset();
- d->key = key;
-}
-
-/*!
- Adds the first \a length chars of \a data to the message.
-*/
-void QMessageAuthenticationCode::addData(const char *data, qsizetype length)
-{
- d->initMessageHash();
- d->messageHash.addData({data, length});
-}
-
-/*!
- \overload addData()
-*/
-void QMessageAuthenticationCode::addData(const QByteArray &data)
-{
- d->initMessageHash();
- d->messageHash.addData(data);
-}
-
-/*!
- Reads the data from the open QIODevice \a device until it ends
- and adds it to message. Returns \c true if reading was successful.
-
- \note \a device must be already opened.
- */
-bool QMessageAuthenticationCode::addData(QIODevice *device)
-{
- d->initMessageHash();
- return d->messageHash.addData(device);
-}
-
-/*!
- Returns the final authentication code.
-
- \sa QByteArray::toHex()
-*/
-QByteArray QMessageAuthenticationCode::result() const
-{
- if (!d->result.isEmpty())
- return d->result;
-
- d->initMessageHash();
-
- const int blockSize = qt_hash_block_size(d->method);
-
- QByteArrayView hashedMessage = d->messageHash.resultView();
-
- QVarLengthArray<char> oKeyPad(blockSize);
- const char * const keyData = d->key.constData();
-
- for (int i = 0; i < blockSize; ++i)
- oKeyPad[i] = keyData[i] ^ 0x5c;
-
- QCryptographicHash hash(d->method);
- hash.addData(oKeyPad);
- hash.addData(hashedMessage);
-
- d->result = hash.result();
- return d->result;
-}
-
-/*!
- Returns the authentication code for the message \a message using
- the key \a key and the method \a method.
-*/
-QByteArray QMessageAuthenticationCode::hash(const QByteArray &message, const QByteArray &key,
- QCryptographicHash::Algorithm method)
-{
- QMessageAuthenticationCode mac(method);
- mac.setKey(key);
- mac.addData(message);
- return mac.result();
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/tools/qmessageauthenticationcode.h b/src/corelib/tools/qmessageauthenticationcode.h
index c1e50b5668..4e88138763 100644
--- a/src/corelib/tools/qmessageauthenticationcode.h
+++ b/src/corelib/tools/qmessageauthenticationcode.h
@@ -12,25 +12,48 @@ QT_BEGIN_NAMESPACE
class QMessageAuthenticationCodePrivate;
class QIODevice;
+// implemented in qcryptographichash.cpp
class Q_CORE_EXPORT QMessageAuthenticationCode
{
public:
+#if QT_CORE_REMOVED_SINCE(6, 6)
explicit QMessageAuthenticationCode(QCryptographicHash::Algorithm method,
- const QByteArray &key = QByteArray());
+ const QByteArray &key);
+#endif
+ explicit QMessageAuthenticationCode(QCryptographicHash::Algorithm method,
+ QByteArrayView key = {});
+
+ QMessageAuthenticationCode(QMessageAuthenticationCode &&other) noexcept
+ : d{std::exchange(other.d, nullptr)} {}
~QMessageAuthenticationCode();
- void reset();
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QMessageAuthenticationCode)
+ void swap(QMessageAuthenticationCode &other) noexcept
+ { qt_ptr_swap(d, other.d); }
+
+ void reset() noexcept;
+#if QT_CORE_REMOVED_SINCE(6, 6)
void setKey(const QByteArray &key);
+#endif
+ void setKey(QByteArrayView key) noexcept;
void addData(const char *data, qsizetype length);
+#if QT_CORE_REMOVED_SINCE(6, 6)
void addData(const QByteArray &data);
+#endif
+ void addData(QByteArrayView data) noexcept;
bool addData(QIODevice *device);
+ QByteArrayView resultView() const noexcept;
QByteArray result() const;
+#if QT_CORE_REMOVED_SINCE(6, 6)
static QByteArray hash(const QByteArray &message, const QByteArray &key,
QCryptographicHash::Algorithm method);
+#endif
+ static QByteArray hash(QByteArrayView message, QByteArrayView key,
+ QCryptographicHash::Algorithm method);
private:
Q_DISABLE_COPY(QMessageAuthenticationCode)
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 f4751fdc45..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
@@ -92,7 +92,7 @@
\snippet code/src_corelib_tools_qmultimap.cpp 11
If you only need to extract the values from a map (not the keys),
- you can also use \l{foreach}:
+ you can also use range-based for:
\snippet code/src_corelib_tools_qmultimap.cpp 12
@@ -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,17 +890,19 @@
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()
*/
/*! \typedef QMultiMap::Iterator
- Qt-style synonym for QMultiMap<Key, T>::iterator.
+ Qt-style synonym for QMultiMap::iterator.
*/
/*! \typedef QMultiMap::ConstIterator
- Qt-style synonym for QMultiMap<Key, T>::const_iterator.
+ Qt-style synonym for QMultiMap::const_iterator.
*/
/*! \typedef QMultiMap::difference_type
@@ -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
*/
@@ -959,6 +965,7 @@
*/
/*! \fn template <class Key, class T> QMultiMap<Key, T> operator+=(QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rhs)
+ \relates QMultiMap
Inserts all the items in the \a rhs map into the \a lhs map and
returns the resulting map.
@@ -967,6 +974,7 @@
*/
/*! \fn template <class Key, class T> QMultiMap<Key, T> operator+(const QMultiMap<Key, T> &lhs, const QMultiMap<Key, T> &rhs)
+ \relates QMultiMap
Returns a map that contains all the items in the \a lhs map in
addition to all the items in \a rhs. If a key is common to both
@@ -979,12 +987,6 @@
\inmodule QtCore
\brief The QMultiMap::iterator class provides an STL-style non-const iterator for QMultiMap.
- QMultiMap features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QMultiMap\<Key, T\>::iterator allows you to iterate over a QMultiMap
and to modify the value (but not the key) stored under
a particular key. If you want to iterate over a const QMultiMap, you
@@ -999,38 +1001,20 @@
start iterating. Here's a typical loop that prints all the (key,
value) pairs stored in a map:
- \snippet code/src_corelib_tools_qmultimap.cpp 18
-
Unlike QMultiHash, which stores its items in an arbitrary order, QMultiMap
stores its items ordered by key. Items that share the same key
will appear consecutively,
from the most recently to the least recently inserted value.
- Let's see a few examples of things we can do with a
- QMultiMap::iterator that we cannot do with a QMultiMap::const_iterator.
Here's an example that increments every value stored in the QMultiMap
by 2:
\snippet code/src_corelib_tools_qmultimap.cpp 19
- Here's an example that removes all the items whose key is a
- string that starts with an underscore character:
-
- \snippet code/src_corelib_tools_qmultimap.cpp 20
-
- The call to QMultiMap::erase() removes the item pointed to by the
- iterator from the map, and returns an iterator to the next item.
- Here's another way of removing an item while iterating:
+ To remove elements from a QMultiMap you can use erase_if(QMultiMap\<Key, T\> &map, Predicate pred):
\snippet code/src_corelib_tools_qmultimap.cpp 21
- It might be tempting to write code like this:
-
- \snippet code/src_corelib_tools_qmultimap.cpp 22
-
- However, this will potentially crash in \c{++i}, because \c i is
- a dangling iterator after the call to erase().
-
Multiple iterators can be used on the same map. If you add items
to the map, existing iterators will remain valid. If you remove
items from the map, iterators that point to the removed items
@@ -1041,7 +1025,7 @@
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QMultiMap::const_iterator, QMultiMap::key_iterator, QMutableMultiMapIterator
+ \sa QMultiMap::const_iterator, QMultiMap::key_iterator, QMultiMap::key_value_iterator
*/
/*! \typedef QMultiMap::iterator::difference_type
@@ -1142,7 +1126,7 @@
/*! \fn template <class Key, class T> QMultiMap<Key, T>::iterator &QMultiMap<Key, T>::iterator::operator++()
- The prefix ++ operator (\c{++i}) advances the iterator to the
+ The prefix \c{++} operator (\c{++i}) advances the iterator to the
next item in the multi map and returns an iterator to the new current
item.
@@ -1155,14 +1139,14 @@
\overload
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the multi map and returns an iterator to the previously
current item.
*/
/*! \fn template <class Key, class T> QMultiMap<Key, T>::iterator &QMultiMap<Key, T>::iterator::operator--()
- The prefix -- operator (\c{--i}) makes the preceding item
+ The prefix \c{--} operator (\c{--i}) makes the preceding item
current and returns an iterator pointing to the new current item.
Calling this function on QMultiMap::begin() leads to undefined
@@ -1175,7 +1159,7 @@
\overload
- The postfix -- operator (\c{i--}) makes the preceding item
+ The postfix \c{--} operator (\c{i--}) makes the preceding item
current and returns an iterator pointing to the previously
current item.
*/
@@ -1202,12 +1186,6 @@
\inmodule QtCore
\brief The QMultiMap::const_iterator class provides an STL-style const iterator for QMultiMap.
- QMultiMap features both \l{STL-style iterators} and \l{Java-style
- iterators}. The STL-style iterators are more low-level and more
- cumbersome to use; on the other hand, they are slightly faster
- and, for developers who already know STL, have the advantage of
- familiarity.
-
QMultiMap\<Key, T\>::const_iterator allows you to iterate over a QMultiMap.
If you want to modify the QMultiMap as you iterate
over it, you must use QMultiMap::iterator instead. It is generally
@@ -1218,12 +1196,16 @@
The default QMultiMap::const_iterator constructor creates an
uninitialized iterator. You must initialize it using a QMultiMap
- function like QMultiMap::constBegin(), QMultiMap::constEnd(), or
- QMultiMap::find() before you can start iterating. Here's a typical
+ function like QMultiMap::cbegin(), QMultiMap::cend(), or
+ QMultiMap::constFind() before you can start iterating. Here's a typical
loop that prints all the (key, value) pairs stored in a map:
\snippet code/src_corelib_tools_qmultimap.cpp 24
+ Here's an example that removes all the items whose value is greater than 10:
+
+ \snippet code/src_corelib_tools_qmultimap.cpp 20
+
Unlike QMultiHash, which stores its items in an arbitrary order, QMultiMap
stores its items ordered by key. Items that share the same key
will appear consecutively,
@@ -1239,7 +1221,7 @@
while iterators are active on that container. For more information,
read \l{Implicit sharing iterator problem}.
- \sa QMultiMap::iterator, QMultiMap::key_iterator, QMultiMapIterator
+ \sa QMultiMap::iterator, QMultiMap::key_iterator, QMultiMap::const_key_value_iterator
*/
/*! \typedef QMultiMap::const_iterator::difference_type
@@ -1316,7 +1298,7 @@
/*! \fn template <class Key, class T> QMultiMap<Key, T>::const_iterator &QMultiMap<Key, T>::const_iterator::operator++()
- The prefix ++ operator (\c{++i}) advances the iterator to the
+ The prefix \c{++} operator (\c{++i}) advances the iterator to the
next item in the map and returns an iterator to the new current
item.
@@ -1329,14 +1311,14 @@
\overload
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the map and returns an iterator to the previously
current item.
*/
/*! \fn template <class Key, class T> QMultiMap<Key, T>::const_iterator &QMultiMap<Key, T>::const_iterator::operator--()
- The prefix -- operator (\c{--i}) makes the preceding item
+ The prefix \c{--} operator (\c{--i}) makes the preceding item
current and returns an iterator pointing to the new current item.
Calling this function on QMultiMap::begin() leads to undefined
@@ -1349,7 +1331,7 @@
\overload
- The postfix -- operator (\c{i--}) makes the preceding item
+ The postfix \c{--} operator (\c{i--}) makes the preceding item
current and returns an iterator pointing to the previously
current item.
*/
@@ -1458,7 +1440,7 @@
/*!
\fn template <class Key, class T> QMultiMap<Key, T>::key_iterator &QMultiMap<Key, T>::key_iterator::operator++()
- The prefix ++ operator (\c{++i}) advances the iterator to the
+ The prefix \c{++} operator (\c{++i}) advances the iterator to the
next item in the hash and returns an iterator to the new current
item.
@@ -1471,14 +1453,14 @@
\overload
- The postfix ++ operator (\c{i++}) advances the iterator to the
+ The postfix \c{++} operator (\c{i++}) advances the iterator to the
next item in the hash and returns an iterator to the previous
item.
*/
/*! \fn template <class Key, class T> QMultiMap<Key, T>::key_iterator &QMultiMap<Key, T>::key_iterator::operator--()
- The prefix -- operator (\c{--i}) makes the preceding item
+ The prefix \c{--} operator (\c{--i}) makes the preceding item
current and returns an iterator pointing to the new current item.
Calling this function on QMultiMap::keyBegin() leads to undefined
@@ -1491,7 +1473,7 @@
\overload
- The postfix -- operator (\c{i--}) makes the preceding item
+ The postfix \c{--} operator (\c{i--}) makes the preceding item
current and returns an iterator pointing to the previous
item.
*/
diff --git a/src/corelib/tools/qoffsetstringarray_p.h b/src/corelib/tools/qoffsetstringarray_p.h
index a3b56badc6..9103606a13 100644
--- a/src/corelib/tools/qoffsetstringarray_p.h
+++ b/src/corelib/tools/qoffsetstringarray_p.h
@@ -20,6 +20,7 @@
#include <QByteArrayView>
+#include <QtCore/q20algorithm.h>
#include <array>
#include <limits>
#include <string_view>
@@ -62,6 +63,15 @@ public:
constexpr int count() const { return int(m_offsets.size()) - 1; }
+ bool contains(QByteArrayView needle, Qt::CaseSensitivity cs = Qt::CaseSensitive) const noexcept
+ {
+ for (qsizetype i = 0; i < count(); ++i) {
+ if (viewAt(i).compare(needle, cs) == 0)
+ return true;
+ }
+ return false;
+ }
+
private:
StaticString m_string;
OffsetList m_offsets;
@@ -69,21 +79,13 @@ private:
};
namespace QtPrivate {
-// std::copy is not constexpr in C++17
-template <typename II, typename OO>
-static constexpr OO copyData(II input, qsizetype n, OO output)
-{
- using E = decltype(+*output);
- for (qsizetype i = 0; i < n; ++i)
- output[i] = E(input[i]);
- return output + n;
-}
-
template <size_t Highest> constexpr auto minifyValue()
{
- if constexpr (Highest <= (std::numeric_limits<quint8>::max)()) {
+ constexpr size_t max8 = (std::numeric_limits<quint8>::max)();
+ constexpr size_t max16 = (std::numeric_limits<quint16>::max)();
+ if constexpr (Highest <= max8) {
return quint8(Highest);
- } else if constexpr (Highest <= (std::numeric_limits<quint16>::max)()) {
+ } else if constexpr (Highest <= max16) {
return quint16(Highest);
} else {
// int is probably enough for everyone
@@ -100,7 +102,7 @@ constexpr auto makeStaticString(Extractor extract, const T &... entries)
const char *strings[] = { extract(entries).operator const char *()... };
size_t lengths[] = { sizeof(extract(T{}))... };
for (size_t i = 0; i < std::size(strings); ++i) {
- copyData(strings[i], lengths[i], result.begin() + offset);
+ q20::copy_n(strings[i], lengths[i], result.begin() + offset);
offset += lengths[i];
}
return result;
@@ -110,7 +112,7 @@ template <size_t N> struct StaticString
{
char value[N] = {};
constexpr StaticString() = default;
- constexpr StaticString(const char (&s)[N]) { copyData(s, N, value); }
+ constexpr StaticString(const char (&s)[N]) { q20::copy_n(s, N, value); }
constexpr operator const char *() const { return value; }
};
@@ -125,10 +127,10 @@ template <size_t KL, size_t VL> struct StaticMapEntry
};
template <typename StringExtractor, typename... T>
-constexpr auto qOffsetStringArray(StringExtractor extractString, const T &... entries)
+constexpr auto makeOffsetStringArray(StringExtractor extractString, const T &... entries)
{
constexpr size_t Count = sizeof...(T);
- constexpr qsizetype StringLength = (sizeof(extractString(T{})) + ...);
+ constexpr size_t StringLength = (sizeof(extractString(T{})) + ...);
using MinifiedOffsetType = decltype(QtPrivate::minifyValue<StringLength>());
size_t offset = 0;
@@ -136,18 +138,20 @@ constexpr auto qOffsetStringArray(StringExtractor extractString, const T &... en
// prepend zero
std::array<MinifiedOffsetType, Count + 1> minifiedOffsetList = {};
- QtPrivate::copyData(fullOffsetList.begin(), Count, minifiedOffsetList.begin() + 1);
+ q20::transform(fullOffsetList.begin(), fullOffsetList.end(),
+ minifiedOffsetList.begin() + 1,
+ [] (auto e) { return MinifiedOffsetType(e); });
std::array staticString = QtPrivate::makeStaticString<StringLength>(extractString, entries...);
return QOffsetStringArray(staticString, minifiedOffsetList);
}
-}
+} // namespace QtPrivate
template<int ... Nx>
constexpr auto qOffsetStringArray(const char (&...strings)[Nx]) noexcept
{
auto extractString = [](const auto &s) -> decltype(auto) { return s; };
- return QtPrivate::qOffsetStringArray(extractString, QtPrivate::StaticString(strings)...);
+ return QtPrivate::makeOffsetStringArray(extractString, QtPrivate::StaticString(strings)...);
}
QT_WARNING_POP
diff --git a/src/corelib/tools/qpair.h b/src/corelib/tools/qpair.h
index 75efaed3cf..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,8 +22,7 @@ constexpr decltype(auto) qMakePair(T1 &&value1, T2 &&value2)
return std::make_pair(std::forward<T1>(value1), std::forward<T2>(value2));
}
-template<class T1, class T2>
-class QTypeInfo<std::pair<T1, T2>> : public QTypeInfoMerger<std::pair<T1, T2>, T1, T2> {};
+#endif // QT_NO_QPAIR
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h
index 48c5e91f62..7df4d49005 100644
--- a/src/corelib/tools/qpoint.h
+++ b/src/corelib/tools/qpoint.h
@@ -6,12 +6,17 @@
#include <QtCore/qnamespace.h>
+#include <QtCore/q20type_traits.h>
+#include <QtCore/q23utility.h>
+
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
struct CGPoint;
#endif
QT_BEGIN_NAMESPACE
+QT_ENABLE_P0846_SEMANTICS_FOR(get)
+
class QPointF;
class QPoint
@@ -86,13 +91,13 @@ private:
template <std::size_t I,
typename P,
std::enable_if_t<(I < 2), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<P>, QPoint>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<P>, QPoint>, bool> = true>
friend constexpr decltype(auto) get(P &&p) noexcept
{
if constexpr (I == 0)
- return (std::forward<P>(p).xp);
+ return q23::forward_like<P>(p.xp);
else if constexpr (I == 1)
- return (std::forward<P>(p).yp);
+ return q23::forward_like<P>(p.yp);
}
};
@@ -283,13 +288,13 @@ private:
template <std::size_t I,
typename P,
std::enable_if_t<(I < 2), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<P>, QPointF>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<P>, QPointF>, bool> = true>
friend constexpr decltype(auto) get(P &&p) noexcept
{
if constexpr (I == 0)
- return (std::forward<P>(p).xp);
+ return q23::forward_like<P>(p.xp);
else if constexpr (I == 1)
- return (std::forward<P>(p).yp);
+ return q23::forward_like<P>(p.yp);
}
};
diff --git a/src/corelib/tools/qrect.h b/src/corelib/tools/qrect.h
index d114fa7ae0..e69a217f48 100644
--- a/src/corelib/tools/qrect.h
+++ b/src/corelib/tools/qrect.h
@@ -16,6 +16,11 @@
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
struct CGRect;
#endif
+#if defined(Q_OS_WASM) || defined(Q_QDOC)
+namespace emscripten {
+class val;
+}
+#endif
QT_BEGIN_NAMESPACE
@@ -586,6 +591,11 @@ public:
[[nodiscard]] CGRect toCGRect() const noexcept;
#endif
+#if defined(Q_OS_WASM) || defined(Q_QDOC)
+ [[nodiscard]] static QRectF fromDOMRect(emscripten::val domRect);
+ [[nodiscard]] emscripten::val toDOMRect() const;
+#endif
+
private:
qreal xp;
qreal yp;
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 e569267c65..515eb9dc75 100644
--- a/src/corelib/tools/qscopedpointer.cpp
+++ b/src/corelib/tools/qscopedpointer.cpp
@@ -269,31 +269,37 @@ 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.
*/
/*!
- \fn template <typename T, typename Cleanup> T *QScopedArrayPointer<T, Cleanup>::operator[](int i)
+ \fn template <typename T, typename Cleanup> T *QScopedArrayPointer<T, Cleanup>::operator[](qsizetype i)
Provides access to entry \a i of the scoped pointer's array of
objects.
If the contained pointer is \nullptr, behavior is undefined.
+ \note In Qt versions prior to 6.5, \a i was of type \c{int}, not
+ \c{qsizetype}, possibly causing truncation on 64-bit platforms.
+
\sa isNull()
*/
/*!
- \fn template <typename T, typename Cleanup> T *QScopedArrayPointer<T, Cleanup>::operator[](int i) const
+ \fn template <typename T, typename Cleanup> T *QScopedArrayPointer<T, Cleanup>::operator[](qsizetype i) const
Provides access to entry \a i of the scoped pointer's array of
objects.
If the contained pointer is \nullptr behavior is undefined.
+ \note In Qt versions prior to 6.5, \a i was of type \c{int}, not
+ \c{qsizetype}, possibly causing truncation on 64-bit platforms.
+
\sa isNull()
*/
diff --git a/src/corelib/tools/qscopedpointer.h b/src/corelib/tools/qscopedpointer.h
index 508816a7e7..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)
{
}
@@ -120,7 +121,7 @@ public:
{
if (d == other)
return;
- T *oldD = qExchange(d, other);
+ T *oldD = std::exchange(d, other);
Cleanup::cleanup(oldD);
}
@@ -128,7 +129,7 @@ public:
QT_DEPRECATED_VERSION_X_6_1("Use std::unique_ptr instead, and call release().")
T *take() noexcept
{
- T *oldD = qExchange(d, nullptr);
+ T *oldD = std::exchange(d, nullptr);
return oldD;
}
#endif
@@ -187,26 +188,28 @@ 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)
{
}
- inline T &operator[](int i)
+ T &operator[](qsizetype i)
{
return this->d[i];
}
- inline const T &operator[](int i) const
+ const T &operator[](qsizetype i) const
{
return this->d[i];
}
diff --git a/src/corelib/tools/qscopedvaluerollback.h b/src/corelib/tools/qscopedvaluerollback.h
index a4a451b0c7..0ae3efd0c0 100644
--- a/src/corelib/tools/qscopedvaluerollback.h
+++ b/src/corelib/tools/qscopedvaluerollback.h
@@ -9,17 +9,20 @@
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(qExchange(var, std::move(value)))
+ : varRef(var), oldValue(std::move(var)) // ### C++20: std::exchange(var, std::move(value))
{
+ var = std::move(value);
}
#if __cpp_constexpr >= 201907L
diff --git a/src/corelib/tools/qscopeguard.h b/src/corelib/tools/qscopeguard.h
index 2639b41222..9be6634cc8 100644
--- a/src/corelib/tools/qscopeguard.h
+++ b/src/corelib/tools/qscopeguard.h
@@ -13,22 +13,25 @@
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(qExchange(other.m_invoke, false))
+ , m_invoke(std::exchange(other.m_invoke, false))
{
}
diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h
index 2a945a31e8..7330b5e91c 100644
--- a/src/corelib/tools/qset.h
+++ b/src/corelib/tools/qset.h
@@ -35,7 +35,7 @@ public:
inline void swap(QSet<T> &other) noexcept { q_hash.swap(other.q_hash); }
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename U = T>
QTypeTraits::compare_eq_result_container<QSet, U> operator==(const QSet<T> &other) const
{ return q_hash == other.q_hash; }
@@ -151,7 +151,7 @@ public:
// more Qt
typedef iterator Iterator;
typedef const_iterator ConstIterator;
- inline qsizetype count() const { return q_hash.count(); }
+ inline qsizetype count() const { return q_hash.size(); }
inline iterator insert(const T &value)
{ return q_hash.insert(value, QHashDummyValue()); }
inline iterator insert(T &&value)
@@ -189,14 +189,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 +228,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;
}
@@ -244,7 +251,7 @@ Q_INLINE_TEMPLATE QSet<T> &QSet<T>::intersect(const QSet<T> &other)
copy2 = *this;
*this = copy1;
}
- for (const auto &e : qAsConst(copy1)) {
+ for (const auto &e : std::as_const(copy1)) {
if (!copy2.contains(e))
remove(e);
}
diff --git a/src/corelib/tools/qset.qdoc b/src/corelib/tools/qset.qdoc
index 47153e1349..4ef7a80a52 100644
--- a/src/corelib/tools/qset.qdoc
+++ b/src/corelib/tools/qset.qdoc
@@ -46,7 +46,7 @@
QSet is unordered, so an iterator's sequence cannot be assumed to
be predictable. If ordering by key is required, use a QMap.
- To navigate through a QSet, you can also use \l{foreach}:
+ To navigate through a QSet, you can also use range-based for:
\snippet code/doc_src_qset.cpp 6
@@ -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).
@@ -435,7 +435,7 @@
*/
/*!
- \fn template <class T> QSet<T>::insert(const T &value)
+ \fn template <class T> QSet<T>::iterator QSet<T>::insert(const T &value)
Inserts item \a value into the set, if \a value isn't already
in the set, and returns an iterator pointing at the inserted
@@ -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 be848e2ef8..4c4153a506 100644
--- a/src/corelib/tools/qshareddata.h
+++ b/src/corelib/tools/qshareddata.h
@@ -49,15 +49,19 @@ public:
const T *data() const noexcept { return d; }
const T *get() const noexcept { return d; }
const T *constData() const noexcept { return d; }
- T *take() noexcept { return qExchange(d, nullptr); }
+ 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(); }
@@ -66,7 +70,7 @@ public:
if (ptr != d) {
if (ptr)
ptr->ref.ref();
- T *old = qExchange(d, ptr);
+ T *old = std::exchange(d, ptr);
if (old && !old->ref.deref())
delete old;
}
@@ -82,7 +86,8 @@ public:
reset(o);
return *this;
}
- QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(qExchange(o.d, nullptr)) {}
+ Q_NODISCARD_CTOR
+ QSharedDataPointer(QSharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedDataPointer)
operator bool () const noexcept { return d != nullptr; }
@@ -135,21 +140,26 @@ public:
T *data() const noexcept { return d; }
T *get() const noexcept { return d; }
const T *constData() const noexcept { return d; }
- T *take() noexcept { return qExchange(d, nullptr); }
+ T *take() noexcept { return std::exchange(d, nullptr); }
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()))
@@ -163,7 +173,7 @@ public:
if (ptr != d) {
if (ptr)
ptr->ref.ref();
- T *old = qExchange(d, ptr);
+ T *old = std::exchange(d, ptr);
if (old && !old->ref.deref())
delete old;
}
@@ -179,7 +189,8 @@ public:
reset(o);
return *this;
}
- QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(qExchange(o.d, nullptr)) {}
+ Q_NODISCARD_CTOR
+ QExplicitlySharedDataPointer(QExplicitlySharedDataPointer &&o) noexcept : d(std::exchange(o.d, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QExplicitlySharedDataPointer)
operator bool () const noexcept { return d != nullptr; }
diff --git a/src/corelib/tools/qshareddata_impl.h b/src/corelib/tools/qshareddata_impl.h
index 61a9d1d105..e0b4695e36 100644
--- a/src/corelib/tools/qshareddata_impl.h
+++ b/src/corelib/tools/qshareddata_impl.h
@@ -51,7 +51,7 @@ public:
}
QExplicitlySharedDataPointerV2(QExplicitlySharedDataPointerV2 &&other) noexcept
- : d(qExchange(other.d, nullptr))
+ : d(std::exchange(other.d, nullptr))
{
}
@@ -92,7 +92,7 @@ public:
constexpr T *take() noexcept
{
- return qExchange(d, nullptr);
+ return std::exchange(d, nullptr);
}
bool isShared() const noexcept
diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp
index 65c1c144f1..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.
@@ -443,6 +441,46 @@
*/
/*!
+ \fn template <class T> QSharedPointer<T>::QSharedPointer(QSharedPointer &&other)
+
+ Move-constructs a QSharedPointer instance, making it point at the same
+ object that \a other was pointing to.
+
+ \since 5.4
+*/
+
+/*!
+ \fn template <class T> QSharedPointer<T>::operator=(QSharedPointer &&other)
+
+ Move-assigns \a other to this QSharedPointer instance.
+
+ \since 5.0
+*/
+
+/*!
+ \fn template <class T> template <class X> QSharedPointer<T>::QSharedPointer(QSharedPointer<X> &&other)
+
+ Move-constructs a QSharedPointer instance, making it point at the same
+ object that \a other was pointing to.
+
+ This constructor participates in overload resolution only if \c{X*}
+ implicitly converts to \c{T*}.
+
+ \since 5.6
+*/
+
+/*!
+ \fn template <class T> template <class X> QSharedPointer<T>::operator=(QSharedPointer<X> &&other)
+
+ Move-assigns \a other to this QSharedPointer instance.
+
+ This assignment operator participates in overload resolution only if \c{X*}
+ implicitly converts to \c{T*}.
+
+ \since 5.6
+*/
+
+/*!
\fn template <class T> QSharedPointer<T>::QSharedPointer(const QWeakPointer<T> &other)
Creates a QSharedPointer by promoting the weak reference \a other
@@ -671,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.
@@ -896,7 +977,16 @@
*/
/*!
- \fn template <class T> template <class X> bool operator==(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)
+ \fn template <class T> qHash(const QSharedPointer<T> &key, size_t seed)
+ \relates QSharedPointer
+
+ Returns the hash value for \a key, using \a seed to seed the calculation.
+
+ \since 5.0
+*/
+
+/*!
+ \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.
@@ -909,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.
@@ -922,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.
@@ -935,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.
@@ -948,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
@@ -962,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
@@ -976,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.
@@ -989,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.
@@ -1002,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.
@@ -1095,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.
@@ -1108,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
@@ -1123,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
@@ -1144,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
@@ -1160,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
@@ -1182,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
@@ -1194,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
@@ -1212,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
@@ -1284,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
@@ -1310,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
@@ -1330,6 +1420,7 @@
QT_BEGIN_NAMESPACE
+QT6_ONLY(
/*!
\internal
This function is called for a just-created QObject \a obj, to enable
@@ -1337,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
@@ -1350,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)
{
@@ -1414,7 +1508,7 @@ QT_END_NAMESPACE
# ifdef QT_SHARED_POINTER_BACKTRACE_SUPPORT
# if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE)
# define BACKTRACE_SUPPORTED
-# elif defined(Q_OS_MAC)
+# elif defined(Q_OS_DARWIN)
# define BACKTRACE_SUPPORTED
# endif
# endif
diff --git a/src/corelib/tools/qsharedpointer.h b/src/corelib/tools/qsharedpointer.h
index a18304cae6..116c9afa00 100644
--- a/src/corelib/tools/qsharedpointer.h
+++ b/src/corelib/tools/qsharedpointer.h
@@ -48,6 +48,11 @@ public:
QSharedPointer<T> &operator=(QSharedPointer<T> &&other) noexcept;
QSharedPointer<T> &operator=(const QWeakPointer<T> &other);
+ template <class X>
+ QSharedPointer(QSharedPointer<X> && other) noexcept;
+ template <class X>
+ QSharedPointer &operator=(QSharedPointer<X> && other) noexcept;
+
void swap(QSharedPointer<T> &other) noexcept;
QWeakPointer<T> toWeakRef() const;
@@ -67,9 +72,25 @@ 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>
+size_t qHash(const QSharedPointer<T> &key, size_t seed = 0) noexcept;
+
+template <class T>
class QWeakPointer
{
public:
@@ -100,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.h b/src/corelib/tools/qsize.h
index 71066c35c0..a5eaf34afe 100644
--- a/src/corelib/tools/qsize.h
+++ b/src/corelib/tools/qsize.h
@@ -8,12 +8,17 @@
#include <QtCore/qhashfunctions.h>
#include <QtCore/qmargins.h>
+#include <QtCore/q20type_traits.h>
+#include <QtCore/q23utility.h>
+
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
struct CGSize;
#endif
QT_BEGIN_NAMESPACE
+// QT_ENABLE_P0846_SEMANTICS_FOR(get) // from qmargins.h
+
class QSizeF;
class Q_CORE_EXPORT QSize
@@ -83,13 +88,13 @@ private:
template <std::size_t I,
typename S,
std::enable_if_t<(I < 2), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<S>, QSize>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<S>, QSize>, bool> = true>
friend constexpr decltype(auto) get(S &&s) noexcept
{
if constexpr (I == 0)
- return (std::forward<S>(s).wd);
+ return q23::forward_like<S>(s.wd);
else if constexpr (I == 1)
- return (std::forward<S>(s).ht);
+ return q23::forward_like<S>(s.ht);
}
};
Q_DECLARE_TYPEINFO(QSize, Q_RELOCATABLE_TYPE);
@@ -272,13 +277,13 @@ private:
template <std::size_t I,
typename S,
std::enable_if_t<(I < 2), bool> = true,
- std::enable_if_t<std::is_same_v<std::decay_t<S>, QSizeF>, bool> = true>
+ std::enable_if_t<std::is_same_v<q20::remove_cvref_t<S>, QSizeF>, bool> = true>
friend constexpr decltype(auto) get(S &&s) noexcept
{
if constexpr (I == 0)
- return (std::forward<S>(s).wd);
+ return q23::forward_like<S>(s.wd);
else if constexpr (I == 1)
- return (std::forward<S>(s).ht);
+ return q23::forward_like<S>(s.ht);
}
};
Q_DECLARE_TYPEINFO(QSizeF, Q_RELOCATABLE_TYPE);
diff --git a/src/corelib/tools/qspan.h b/src/corelib/tools/qspan.h
new file mode 100644
index 0000000000..d6ae2570ae
--- /dev/null
+++ b/src/corelib/tools/qspan.h
@@ -0,0 +1,452 @@
+// 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); }
+
+}; // 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..472f122877
--- /dev/null
+++ b/src/corelib/tools/qspan.qdoc
@@ -0,0 +1,651 @@
+// 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()
+*/
+
+#if 0 // needs fix for QTBUG-118080 integrated into qt5.git
+/*!
+ \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()
+*/
+#endif
+
+//
+// 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)
+*/
+
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 504645993a..6c467d59f8 100644
--- a/src/corelib/tools/qtaggedpointer.h
+++ b/src/corelib/tools/qtaggedpointer.h
@@ -21,7 +21,7 @@ namespace QtPrivate {
static_assert((alignment & (alignment - 1)) == 0,
"Alignment of template parameter must be power of two");
- static constexpr quint8 tagBits = QtPrivate::qConstexprCountTrailingZeroBits(alignment);
+ static constexpr quint8 tagBits = quint8{QtPrivate::qConstexprCountTrailingZeroBits(alignment)};
static_assert(tagBits > 0,
"Alignment of template parameter does not allow any tags");
@@ -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));
@@ -72,12 +72,31 @@ public:
return !isNull();
}
- QTaggedPointer &operator=(T *other) noexcept
+#ifdef Q_QDOC
+ QTaggedPointer &operator=(T *other) noexcept;
+#else
+ // Disables the usage of `ptr = {}`, which would go through this operator
+ // (rather than using the implicitly-generated assignment operator).
+ // The operators have different semantics: the ones here leave the tag intact,
+ // the implicitly-generated one overwrites it.
+ template <typename U,
+ std::enable_if_t<std::is_convertible_v<U *, T *>, bool> = false>
+ QTaggedPointer &operator=(U *other) noexcept
{
- d = reinterpret_cast<quintptr>(other) | (d & tagMask());
+ T *otherT = other;
+ d = reinterpret_cast<quintptr>(otherT) | (d & tagMask());
return *this;
}
+ template <typename U,
+ std::enable_if_t<std::is_null_pointer_v<U>, bool> = false>
+ QTaggedPointer &operator=(U) noexcept
+ {
+ d = reinterpret_cast<quintptr>(static_cast<T *>(nullptr)) | (d & tagMask());
+ return *this;
+ }
+#endif
+
static constexpr Tag maximumTag() noexcept
{
return TagType(typename QtPrivate::TagInfo<T>::TagType(tagMask()));
@@ -85,8 +104,10 @@ public:
void setTag(Tag tag)
{
- Q_ASSERT_X((static_cast<typename QtPrivate::TagInfo<T>::TagType>(tag) & pointerMask()) == 0,
- "QTaggedPointer<T, Tag>::setTag", "Tag is larger than allowed by number of available tag bits");
+ Q_ASSERT_X(
+ (static_cast<quintptr>(tag) & pointerMask()) == 0,
+ "QTaggedPointer<T, Tag>::setTag",
+ "Tag is larger than allowed by number of available tag bits");
d = (d & pointerMask()) | static_cast<quintptr>(tag);
}
diff --git a/src/corelib/tools/qtaggedpointer.qdoc b/src/corelib/tools/qtaggedpointer.qdoc
index acabf7047f..f910e35b69 100644
--- a/src/corelib/tools/qtaggedpointer.qdoc
+++ b/src/corelib/tools/qtaggedpointer.qdoc
@@ -187,7 +187,7 @@
*/
/*!
- \fn template <typename T, typename Tag> qHash(QTaggedPointer<T, Tag> key, std::size_t seed)
+ \fn template <typename T, typename Tag> qHash(QTaggedPointer<T, Tag> key, std::size_t seed = 0)
\relates QTaggedPointer
Returns the hash value for the \a key, using \a seed to seed the calculation.
diff --git a/src/corelib/tools/qtimeline.cpp b/src/corelib/tools/qtimeline.cpp
index 337f09cbb8..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()
@@ -290,7 +292,7 @@ QTimeLine::State QTimeLine::state() const
\property QTimeLine::loopCount
\brief the number of times the timeline should loop before it's finished.
- A loop count of of 0 means that the timeline will loop forever.
+ A loop count of 0 means that the timeline will loop forever.
By default, this property contains a value of 1.
*/
@@ -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 338f89d633..105aa40c02 100644
--- a/src/corelib/tools/qtools_p.h
+++ b/src/corelib/tools/qtools_p.h
@@ -16,22 +16,32 @@
//
#include "QtCore/private/qglobal_p.h"
+
+#include <chrono>
#include <limits.h>
+#include <time.h>
QT_BEGIN_NAMESPACE
namespace QtMiscUtils {
-constexpr inline char toHexUpper(uint value) noexcept
+constexpr inline char toHexUpper(char32_t value) noexcept
{
return "0123456789ABCDEF"[value & 0xF];
}
-constexpr inline char toHexLower(uint value) noexcept
+constexpr inline char toHexLower(char32_t value) noexcept
{
return "0123456789abcdef"[value & 0xF];
}
-constexpr inline int fromHex(uint c) noexcept
+[[nodiscard]] constexpr inline bool isHexDigit(char32_t c) noexcept
+{
+ return (c >= '0' && c <= '9')
+ || (c >= 'A' && c <= 'F')
+ || (c >= 'a' && c <= 'f');
+}
+
+constexpr inline int fromHex(char32_t c) noexcept
{
return ((c >= '0') && (c <= '9')) ? int(c - '0') :
((c >= 'A') && (c <= 'F')) ? int(c - 'A' + 10) :
@@ -39,19 +49,49 @@ constexpr inline int fromHex(uint c) noexcept
/* otherwise */ -1;
}
-constexpr inline char toOct(uint value) noexcept
+constexpr inline char toOct(char32_t value) noexcept
{
return char('0' + (value & 0x7));
}
-constexpr inline int fromOct(uint c) noexcept
+[[nodiscard]] constexpr inline bool isOctalDigit(char32_t c) noexcept
+{
+ return c >= '0' && c <= '7';
+}
+
+constexpr inline int fromOct(char32_t c) noexcept
+{
+ return isOctalDigit(c) ? int(c - '0') : -1;
+}
+
+[[nodiscard]] constexpr inline bool isAsciiDigit(char32_t c) noexcept
+{
+ return c >= '0' && c <= '9';
+}
+
+constexpr inline bool isAsciiUpper(char32_t c) noexcept
{
- return ((c >= '0') && (c <= '7')) ? int(c - '0') : -1;
+ return c >= 'A' && c <= 'Z';
+}
+
+constexpr inline bool isAsciiLower(char32_t c) noexcept
+{
+ return c >= 'a' && c <= 'z';
+}
+
+constexpr inline bool isAsciiLetterOrNumber(char32_t c) noexcept
+{
+ return isAsciiDigit(c) || isAsciiLower(c) || isAsciiUpper(c);
}
constexpr inline char toAsciiLower(char ch) noexcept
{
- return (ch >= 'A' && ch <= 'Z') ? ch - 'A' + 'a' : ch;
+ return isAsciiUpper(ch) ? ch - 'A' + 'a' : ch;
+}
+
+constexpr inline char toAsciiUpper(char ch) noexcept
+{
+ return isAsciiLower(ch) ? ch - 'a' + 'A' : ch;
}
constexpr inline int caseCompareAscii(char lhs, char rhs) noexcept
@@ -61,11 +101,19 @@ constexpr inline int caseCompareAscii(char lhs, char rhs) noexcept
return int(uchar(lhsLower)) - int(uchar(rhsLower));
}
+constexpr inline int isAsciiPrintable(char32_t ch) noexcept
+{
+ return ch >= ' ' && ch < 0x7f;
+}
+constexpr inline int qt_lencmp(qsizetype lhs, qsizetype rhs) noexcept
+{
+ return lhs == rhs ? 0 :
+ lhs > rhs ? 1 :
+ /* else */ -1 ;
}
-// We typically need an extra bit for qNextPowerOfTwo when determining the next allocation size.
-constexpr qsizetype MaxAllocSize = (std::numeric_limits<qsizetype>::max)();
+} // namespace QtMiscUtils
struct CalculateGrowingBlockSizeResult
{
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 54603bcec1..afc345d3be 100644
--- a/src/corelib/tools/qvarlengtharray.h
+++ b/src/corelib/tools/qvarlengtharray.h
@@ -18,7 +18,7 @@
#include <algorithm>
#include <initializer_list>
#include <iterator>
-#include <memory>
+#include <QtCore/q20memory.h>
#include <new>
#include <string.h>
@@ -52,7 +52,8 @@ protected:
qsizetype s; // size
void *ptr; // data
- Q_ALWAYS_INLINE constexpr void verify(qsizetype pos = 0, qsizetype n = 1) const
+ 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());
@@ -172,20 +173,35 @@ public:
template <typename Predicate>
qsizetype removeIf(Predicate pred);
+ void clear()
+ {
+ if constexpr (QTypeInfo<T>::isComplex)
+ std::destroy_n(data(), size());
+ s = 0;
+ }
+
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);
}
protected:
+ void growBy(qsizetype prealloc, void *array, qsizetype increment)
+ { reallocate_impl(prealloc, array, size(), (std::max)(size() * 2, size() + increment)); }
template <typename...Args>
reference emplace_back_impl(qsizetype prealloc, void *array, Args&&...args)
{
if (size() == capacity()) // ie. size() != 0
- reallocate_impl(prealloc, array, size(), size() << 1);
- reference r = *new (end()) T(std::forward<Args>(args)...);
+ growBy(prealloc, array, 1);
+ reference r = *q20::construct_at(end(), std::forward<Args>(args)...);
++s;
return r;
}
@@ -206,9 +222,36 @@ protected:
}
void append_impl(qsizetype prealloc, void *array, const T *buf, qsizetype n);
- void reallocate_impl(qsizetype prealloc, void *array, qsizetype size, qsizetype alloc, const T *v = nullptr);
- void resize_impl(qsizetype prealloc, void *array, qsizetype sz, const T *v = nullptr)
- { reallocate_impl(prealloc, array, sz, qMax(sz, capacity()), v); }
+ void reallocate_impl(qsizetype prealloc, void *array, qsizetype size, qsizetype alloc);
+ void resize_impl(qsizetype prealloc, void *array, qsizetype sz, const T &v)
+ {
+ if (QtPrivate::q_points_into_range(&v, begin(), end())) {
+ resize_impl(prealloc, array, sz, T(v));
+ return;
+ }
+ reallocate_impl(prealloc, array, sz, qMax(sz, capacity()));
+ while (size() < sz) {
+ q20::construct_at(data() + size(), v);
+ ++s;
+ }
+ }
+ void resize_impl(qsizetype prealloc, void *array, qsizetype sz)
+ {
+ reallocate_impl(prealloc, array, sz, qMax(sz, capacity()));
+ if constexpr (QTypeInfo<T>::isComplex) {
+ // call default constructor for new objects (which can throw)
+ while (size() < sz) {
+ q20::construct_at(data() + size());
+ ++s;
+ }
+ } else {
+ s = sz;
+ }
+ }
+
+ void assign_impl(qsizetype prealloc, void *array, qsizetype n, const T &t);
+ template <typename Iterator>
+ void assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last);
bool isValidIterator(const const_iterator &i) const
{
@@ -220,19 +263,29 @@ protected:
// Prealloc = 256 by default, specified in qcontainerfwd.h
template<class T, qsizetype Prealloc>
class QVarLengthArray
- : public QVLABase<T>, // ### Qt 7: swap base class order
+#if QT_VERSION >= QT_VERSION_CHECK(7,0,0) || defined(QT_BOOTSTRAPPED)
+ : public QVLAStorage<sizeof(T), alignof(T), Prealloc>,
+ public QVLABase<T>
+#else
+ : public QVLABase<T>,
public QVLAStorage<sizeof(T), alignof(T), Prealloc>
+#endif
{
template <class S, qsizetype Prealloc2>
friend class QVarLengthArray;
using Base = QVLABase<T>;
using Storage = QVLAStorage<sizeof(T), alignof(T), Prealloc>;
+ static_assert(Prealloc > 0, "QVarLengthArray Prealloc must be greater than 0.");
static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
using Base::verify;
template <typename U>
using if_copyable = std::enable_if_t<std::is_copy_constructible_v<U>, bool>;
+ 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;
@@ -293,7 +346,7 @@ public:
{
}
- template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
+ template <typename InputIterator, if_input_iterator<InputIterator> = true>
inline QVarLengthArray(InputIterator first, InputIterator last)
: QVarLengthArray()
{
@@ -340,9 +393,7 @@ public:
QVarLengthArray<T, Prealloc> &operator=(std::initializer_list<T> list)
{
- resize(qsizetype(list.size()));
- std::copy(list.begin(), list.end(),
- QT_MAKE_CHECKED_ARRAY_ITERATOR(begin(), size()));
+ assign(list);
return *this;
}
@@ -352,6 +403,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(); }
@@ -378,8 +430,11 @@ public:
template <typename U = T, if_copyable<U> = true>
#endif
void resize(qsizetype sz, const T &v)
- { Base::resize_impl(Prealloc, this->array, sz, &v); }
+ { Base::resize_impl(Prealloc, this->array, sz, v); }
+ using Base::clear;
+#ifdef Q_QDOC
inline void clear() { resize(0); }
+#endif
void squeeze() { reallocate(size(), size()); }
using Base::capacity;
@@ -454,6 +509,15 @@ public:
void insert(qsizetype i, T &&t);
void insert(qsizetype i, const T &t);
void insert(qsizetype i, qsizetype n, const T &t);
+
+ QVarLengthArray &assign(qsizetype n, const T &t)
+ { Base::assign_impl(Prealloc, this->array, n, t); return *this; }
+ template <typename InputIterator, if_input_iterator<InputIterator> = true>
+ QVarLengthArray &assign(InputIterator first, InputIterator last)
+ { Base::assign_impl(Prealloc, this->array, first, last); return *this; }
+ QVarLengthArray &assign(std::initializer_list<T> list)
+ { assign(list.begin(), list.end()); return *this; }
+
#ifdef Q_QDOC
void replace(qsizetype i, const T &t);
void remove(qsizetype i, qsizetype n = 1);
@@ -618,23 +682,22 @@ QVarLengthArray(InputIterator, InputIterator) -> QVarLengthArray<ValueType>;
template <class T, qsizetype Prealloc>
Q_INLINE_TEMPLATE QVarLengthArray<T, Prealloc>::QVarLengthArray(qsizetype asize)
+ : QVarLengthArray()
{
- this->s = asize;
- static_assert(Prealloc > 0, "QVarLengthArray Prealloc must be greater than 0.");
- Q_ASSERT_X(size() >= 0, "QVarLengthArray::QVarLengthArray()", "Size must be greater than or equal to 0.");
- if (size() > Prealloc) {
- this->ptr = malloc(size() * sizeof(T));
- Q_CHECK_PTR(data());
- this->a = size();
- } else {
- this->ptr = this->array;
- this->a = Prealloc;
- }
- if constexpr (QTypeInfo<T>::isComplex) {
- T *i = end();
- while (i != begin())
- new (--i) T;
+ Q_ASSERT_X(asize >= 0, "QVarLengthArray::QVarLengthArray(qsizetype)",
+ "Size must be greater than or equal to 0.");
+
+ // historically, this ctor worked for non-copyable/non-movable T, so keep it working, why not?
+ // resize(asize) // this requires a movable or copyable T, can't use, need to do it by hand
+
+ if (asize > Prealloc) {
+ this->ptr = malloc(asize * sizeof(T));
+ Q_CHECK_PTR(this->ptr);
+ this->a = asize;
}
+ if constexpr (QTypeInfo<T>::isComplex)
+ std::uninitialized_default_construct_n(data(), asize);
+ this->s = asize;
}
template <class T>
@@ -695,7 +758,7 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::append_impl(qsizetype prealloc, void *arr
const qsizetype asize = size() + increment;
if (asize >= capacity())
- reallocate_impl(prealloc, array, size(), qMax(size() * 2, asize));
+ growBy(prealloc, array, increment);
if constexpr (QTypeInfo<T>::isComplex)
std::uninitialized_copy_n(abuf, increment, end());
@@ -706,7 +769,62 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::append_impl(qsizetype prealloc, void *arr
}
template <class T>
-Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void *array, qsizetype asize, qsizetype aalloc, const T *v)
+Q_OUTOFLINE_TEMPLATE void QVLABase<T>::assign_impl(qsizetype prealloc, void *array, qsizetype n, const T &t)
+{
+ Q_ASSERT(n >= 0);
+ if (n > capacity()) {
+ reallocate_impl(prealloc, array, 0, capacity()); // clear
+ resize_impl(prealloc, array, n, t);
+ } else {
+ auto mid = (std::min)(n, size());
+ std::fill(data(), data() + mid, t);
+ std::uninitialized_fill(data() + mid, data() + n, t);
+ s = n;
+ erase(data() + n, data() + size());
+ }
+}
+
+template <class T>
+template <typename Iterator>
+Q_OUTOFLINE_TEMPLATE void QVLABase<T>::assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last)
+{
+ // This function only provides the basic exception guarantee.
+ constexpr bool IsFwdIt =
+ std::is_convertible_v<typename std::iterator_traits<Iterator>::iterator_category,
+ std::forward_iterator_tag>;
+ if constexpr (IsFwdIt) {
+ const qsizetype n = std::distance(first, last);
+ if (n > capacity())
+ reallocate_impl(prealloc, array, 0, n); // clear & reserve n
+ }
+
+ auto dst = begin();
+ const auto dend = end();
+ while (true) {
+ if (first == last) { // ran out of elements to assign
+ std::destroy(dst, dend);
+ break;
+ }
+ if (dst == dend) { // ran out of existing elements to overwrite
+ if constexpr (IsFwdIt) {
+ dst = std::uninitialized_copy(first, last, dst);
+ break;
+ } else {
+ do {
+ emplace_back_impl(prealloc, array, *first);
+ } while (++first != last);
+ return; // size() is already correct (and dst invalidated)!
+ }
+ }
+ *dst = *first; // overwrite existing element
+ ++dst;
+ ++first;
+ }
+ this->s = dst - begin();
+}
+
+template <class T>
+Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void *array, qsizetype asize, qsizetype aalloc)
{
Q_ASSERT(aalloc >= asize);
Q_ASSERT(data());
@@ -714,7 +832,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;
@@ -747,26 +865,6 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void
if (oldPtr != reinterpret_cast<T *>(array) && oldPtr != data())
free(oldPtr);
-
- if (v) {
- if constexpr (std::is_copy_constructible_v<T>) {
- while (size() < asize) {
- new (data() + size()) T(*v);
- ++s;
- }
- } else {
- Q_UNREACHABLE();
- }
- } else if constexpr (QTypeInfo<T>::isComplex) {
- // call default constructor for new objects (which can throw)
- while (size() < asize) {
- new (data() + size()) T;
- ++s;
- }
- } else {
- s = asize;
- }
-
}
template <class T>
@@ -834,29 +932,12 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::emplace_impl(qsizetype prealloc, void *ar
Q_ASSERT(size() <= capacity());
Q_ASSERT(capacity() > 0);
- qsizetype offset = qsizetype(before - cbegin());
- if (size() == capacity())
- reallocate_impl(prealloc, array, size(), size() * 2);
- if constexpr (!QTypeInfo<T>::isRelocatable) {
- T *b = begin() + offset;
- T *i = end();
- T *j = i + 1;
- // The new end-element needs to be constructed, the rest must be move assigned
- if (i != b) {
- new (--j) T(std::move(*--i));
- while (i != b)
- *--j = std::move(*--i);
- *b = T(std::forward<Args>(args)...);
- } else {
- new (b) T(std::forward<Args>(args)...);
- }
- } else {
- T *b = begin() + offset;
- memmove(static_cast<void *>(b + 1), static_cast<const void *>(b), (size() - offset) * sizeof(T));
- new (b) T(std::forward<Args>(args)...);
- }
- this->s += 1;
- return data() + offset;
+ const qsizetype offset = qsizetype(before - cbegin());
+ emplace_back_impl(prealloc, array, std::forward<Args>(args)...);
+ const auto b = begin() + offset;
+ const auto e = end();
+ QtPrivate::q_rotate(b, e - 1, e);
+ return b;
}
template <class T>
@@ -864,28 +945,12 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::insert_impl(qsizetype prealloc, void *arr
{
Q_ASSERT_X(isValidIterator(before), "QVarLengthArray::insert", "The specified const_iterator argument 'before' is invalid");
- qsizetype offset = qsizetype(before - cbegin());
- if (n != 0) {
- const T copy(t); // `t` could alias an element in [begin(), end()[
- resize_impl(prealloc, array, size() + n);
- if constexpr (!QTypeInfo<T>::isRelocatable) {
- T *b = begin() + offset;
- T *j = end();
- T *i = j - n;
- while (i != b)
- *--j = *--i;
- i = b + n;
- while (i != b)
- *--i = copy;
- } else {
- T *b = begin() + offset;
- T *i = b + n;
- memmove(static_cast<void *>(i), static_cast<const void *>(b), (size() - offset - n) * sizeof(T));
- while (i != b)
- new (--i) T(copy);
- }
- }
- return data() + offset;
+ const qsizetype offset = qsizetype(before - cbegin());
+ resize_impl(prealloc, array, size() + n, t);
+ const auto b = begin() + offset;
+ const auto e = end();
+ QtPrivate::q_rotate(b, e - n, e);
+ return b;
}
template <class T>
diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc
index 4f41304c03..4467e0c65a 100644
--- a/src/corelib/tools/qvarlengtharray.qdoc
+++ b/src/corelib/tools/qvarlengtharray.qdoc
@@ -98,16 +98,17 @@
\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> 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).
+ This constructor only participates in overload resolution if
+ \c InputIterator meets the requirements of an
+ \l {https://en.cppreference.com/w/cpp/named_req/InputIterator} {LegacyInputIterator}.
+
The value type of \c InputIterator must be convertible to \c T.
*/
@@ -139,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
@@ -420,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)
@@ -464,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
@@ -992,3 +1008,40 @@
\sa erase()
*/
+
+/*! \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.
+
+ The size of this container will be equal to \a n. This function will only
+ allocate memory if \a n exceeds the capacity of the container.
+*/
+
+/*! \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
+ iterator range [\a first, \a last).
+
+ The size of this container will be equal to the number of elements in the
+ range [\a first, \a last). This function will only allocate memory if the
+ number of elements in the range exceeds the capacity of the container.
+
+ This function overload only participates in overload resolution if
+ \c InputIterator meets the requirements of an
+ \l {https://en.cppreference.com/w/cpp/named_req/InputIterator} {LegacyInputIterator}.
+
+ The behavior is undefined if either argument is an iterator into *this.
+*/
+
+/*! \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.
+
+ The size of this container will be equal to the number of elements in \a list.
+
+ This function only allocates memory if the number of elements in \a list
+ exceeds the capacity of the container.
+*/
diff --git a/src/corelib/tools/qversionnumber.cpp b/src/corelib/tools/qversionnumber.cpp
index 7d96b43898..4b8ace71cc 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
@@ -72,25 +71,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 +175,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.
@@ -404,19 +450,18 @@ static QVersionNumber from_string(QLatin1StringView string, qsizetype *suffixInd
QVarLengthArray<int, 32> seg;
const char *start = string.begin();
- const char *end = start;
const char *lastGoodEnd = start;
const char *endOfString = string.end();
do {
- bool ok = false;
- const qulonglong value = qstrntoull(start, endOfString - start, &end, 10, &ok);
- if (!ok || value > qulonglong(std::numeric_limits<int>::max()))
+ // parsing as unsigned so a minus sign is rejected
+ auto [value, used] = qstrntoull(start, endOfString - start, 10);
+ if (used <= 0 || value > qulonglong(std::numeric_limits<int>::max()))
break;
seg.append(int(value));
- start = end + 1;
- lastGoodEnd = end;
- } while (start < endOfString && end < endOfString && *end == '.');
+ start += used + 1;
+ lastGoodEnd = start - 1;
+ } while (start < endOfString && *lastGoodEnd == '.');
if (suffixIndex)
*suffixIndex = lastGoodEnd - string.begin();
@@ -525,7 +570,6 @@ QDebug operator<<(QDebug debug, const QVersionNumber &version)
#endif
/*!
- \fn size_t qHash(const QVersionNumber &key, size_t seed)
\relates QHash
\since 5.6
@@ -540,199 +584,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
-
-/*!
- \fn size_t qHash(const QTypeRevision &key, size_t seed)
- \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..95217a6eff 100644
--- a/src/corelib/tools/qversionnumber.h
+++ b/src/corelib/tools/qversionnumber.h
@@ -6,12 +6,16 @@
#ifndef QVERSIONNUMBER_H
#define QVERSIONNUMBER_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 +114,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 +192,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 +307,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;
@@ -307,160 +386,8 @@ Q_DECLARE_TYPEINFO(QVersionNumber, Q_RELOCATABLE_TYPE);
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)
- {
- // 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);
- }
-
- [[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;
-#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(QVersionNumber, Q_CORE_EXPORT)
-QT_DECL_METATYPE_EXTERN(QTypeRevision, Q_CORE_EXPORT)
#endif // QVERSIONNUMBER_H