diff options
Diffstat (limited to 'src/corelib/tools')
-rw-r--r-- | src/corelib/tools/qarraydata.cpp | 2 | ||||
-rw-r--r-- | src/corelib/tools/qhash.h | 252 | ||||
-rw-r--r-- | src/corelib/tools/qlist.cpp | 7 | ||||
-rw-r--r-- | src/corelib/tools/qlist.h | 18 | ||||
-rw-r--r-- | src/corelib/tools/qset.h | 70 | ||||
-rw-r--r-- | src/corelib/tools/qset.qdoc | 8 | ||||
-rw-r--r-- | src/corelib/tools/qsimd.cpp | 133 |
7 files changed, 399 insertions, 91 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 234a44f6b6..36a221f728 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -143,7 +143,7 @@ qCalculateGrowingBlockSize(size_t elementCount, size_t elementSize, size_t heade } result.elementCount = (bytes - unsigned(headerSize)) / unsigned(elementSize); - result.size = bytes; + result.size = result.elementCount * elementSize + headerSize; return result; } diff --git a/src/corelib/tools/qhash.h b/src/corelib/tools/qhash.h index 236e433101..9108be4dc6 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -325,7 +325,11 @@ public: QHashData::Node *i; public: +#if QT_DEPRECATED_SINCE(5, 15) typedef std::bidirectional_iterator_tag iterator_category; +#else + typedef std::forward_iterator_tag iterator_category; +#endif typedef qptrdiff difference_type; typedef T value_type; typedef T *pointer; @@ -350,21 +354,25 @@ public: i = QHashData::nextNode(i); return r; } - inline iterator &operator--() { +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED iterator &operator--() + { i = QHashData::previousNode(i); return *this; } - inline iterator operator--(int) { + inline QT_DEPRECATED iterator operator--(int) + { iterator r = *this; i = QHashData::previousNode(i); return r; } - inline iterator operator+(int j) const + inline QT_DEPRECATED iterator operator+(int j) const { iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } - inline iterator operator-(int j) const { return operator+(-j); } - inline iterator &operator+=(int j) { return *this = *this + j; } - inline iterator &operator-=(int j) { return *this = *this - j; } - friend inline iterator operator+(int j, iterator k) { return k + j; } + inline QT_DEPRECATED iterator operator-(int j) const { return operator+(-j); } + inline QT_DEPRECATED iterator &operator+=(int j) { return *this = *this + j; } + inline QT_DEPRECATED iterator &operator-=(int j) { return *this = *this - j; } + friend inline QT_DEPRECATED iterator operator+(int j, iterator k) { return k + j; } +#endif #ifndef QT_STRICT_ITERATORS public: @@ -384,7 +392,11 @@ public: QHashData::Node *i; public: +#if QT_DEPRECATED_SINCE(5, 15) typedef std::bidirectional_iterator_tag iterator_category; +#else + typedef std::forward_iterator_tag iterator_category; +#endif typedef qptrdiff difference_type; typedef T value_type; typedef const T *pointer; @@ -416,21 +428,28 @@ public: i = QHashData::nextNode(i); return r; } - inline const_iterator &operator--() { +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED const_iterator &operator--() + { i = QHashData::previousNode(i); return *this; } - inline const_iterator operator--(int) { + inline QT_DEPRECATED const_iterator operator--(int) + { const_iterator r = *this; i = QHashData::previousNode(i); return r; } - inline const_iterator operator+(int j) const + inline QT_DEPRECATED const_iterator operator+(int j) const { const_iterator r = *this; if (j > 0) while (j--) ++r; else while (j++) --r; return r; } - inline const_iterator operator-(int j) const { return operator+(-j); } - inline const_iterator &operator+=(int j) { return *this = *this + j; } - inline const_iterator &operator-=(int j) { return *this = *this - j; } - friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } + inline QT_DEPRECATED const_iterator operator-(int j) const { return operator+(-j); } + inline QT_DEPRECATED const_iterator &operator+=(int j) { return *this = *this + j; } + inline QT_DEPRECATED const_iterator &operator-=(int j) { return *this = *this - j; } + friend inline QT_DEPRECATED const_iterator operator+(int j, const_iterator k) + { + return k + j; + } +#endif // ### Qt 5: not sure this is necessary anymore #ifdef QT_STRICT_ITERATORS @@ -462,8 +481,14 @@ public: inline key_iterator &operator++() { ++i; return *this; } inline key_iterator operator++(int) { return key_iterator(i++);} - inline key_iterator &operator--() { --i; return *this; } - inline key_iterator operator--(int) { return key_iterator(i--); } +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED key_iterator &operator--() + { + --i; + return *this; + } + inline QT_DEPRECATED key_iterator operator--(int) { return key_iterator(i--); } +#endif const_iterator base() const { return i; } }; @@ -587,12 +612,31 @@ Q_INLINE_TEMPLATE QHash<Key, T> &QHash<Key, T>::unite(const QHash &other) if (d == &QHashData::shared_null) { *this = other; } else { +#if QT_DEPRECATED_SINCE(5, 15) QHash copy(other); const_iterator it = copy.constEnd(); while (it != copy.constBegin()) { + QT_WARNING_PUSH + QT_WARNING_DISABLE_DEPRECATED --it; + QT_WARNING_POP insertMulti(it.key(), it.value()); } +#else + QHash copy(other); + const_iterator it = copy.cbegin(); + const const_iterator end = copy.cend(); + while (it != end) { + const auto rangeStart = it++; + while (it != end && rangeStart.key() == it.key()) + ++it; + const qint64 last = std::distance(rangeStart, it) - 1; + for (qint64 i = last; i >= 0; --i) { + auto next = std::next(rangeStart, i); + insertMulti(next.key(), next.value()); + } + } +#endif } return *this; } @@ -1145,8 +1189,180 @@ Q_INLINE_TEMPLATE int QMultiHash<Key, T>::count(const Key &key, const T &value) return n; } -Q_DECLARE_ASSOCIATIVE_ITERATOR(Hash) -Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Hash) +template<class Key, class T> +class QHashIterator +{ + typedef typename QHash<Key, T>::const_iterator const_iterator; + typedef const_iterator Item; + QHash<Key, T> c; + const_iterator i, n; + inline bool item_exists() const { return n != c.constEnd(); } + +public: + inline QHashIterator(const QHash<Key, T> &container) + : c(container), i(c.constBegin()), n(c.constEnd()) + { + } + inline QHashIterator &operator=(const QHash<Key, T> &container) + { + c = container; + i = c.constBegin(); + n = c.constEnd(); + return *this; + } + inline void toFront() + { + i = c.constBegin(); + n = c.constEnd(); + } + inline void toBack() + { + i = c.constEnd(); + n = c.constEnd(); + } + inline bool hasNext() const { return i != c.constEnd(); } + inline Item next() + { + n = i++; + return n; + } + inline Item peekNext() const { return i; } + 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; + } +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED bool hasPrevious() const { return i != c.constBegin(); } + inline QT_DEPRECATED Item previous() + { + n = --i; + return n; + } + inline QT_DEPRECATED Item peekPrevious() const + { + const_iterator p = i; + return --p; + } + inline bool QT_DEPRECATED findPrevious(const T &t) + { + while (i != c.constBegin()) + if (*(n = --i) == t) + return true; + n = c.constEnd(); + return false; + } +#endif +}; + +template<class Key, class T> +class QMutableHashIterator +{ + typedef typename QHash<Key, T>::iterator iterator; + typedef typename QHash<Key, T>::const_iterator const_iterator; + typedef iterator Item; + QHash<Key, T> *c; + iterator i, n; + inline bool item_exists() const { return const_iterator(n) != c->constEnd(); } + +public: + inline QMutableHashIterator(QHash<Key, T> &container) : c(&container) + { + i = c->begin(); + n = c->end(); + } + inline QMutableHashIterator &operator=(QHash<Key, T> &container) + { + c = &container; + i = c->begin(); + n = c->end(); + return *this; + } + inline void toFront() + { + i = c->begin(); + n = c->end(); + } + inline void toBack() + { + i = c->end(); + n = c->end(); + } + inline bool hasNext() const { return const_iterator(i) != c->constEnd(); } + inline Item next() + { + n = i++; + return n; + } + inline Item peekNext() const { return i; } + inline void remove() + { + if (const_iterator(n) != c->constEnd()) { + i = c->erase(n); + n = c->end(); + } + } + inline void setValue(const T &t) + { + if (const_iterator(n) != c->constEnd()) + *n = t; + } + inline T &value() + { + Q_ASSERT(item_exists()); + return *n; + } + 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 (const_iterator(n = i) != c->constEnd()) + if (*i++ == t) + return true; + return false; + } +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED bool hasPrevious() const { return const_iterator(i) != c->constBegin(); } + inline QT_DEPRECATED Item previous() + { + n = --i; + return n; + } + inline QT_DEPRECATED Item peekPrevious() const + { + iterator p = i; + return --p; + } + inline QT_DEPRECATED bool findPrevious(const T &t) + { + while (const_iterator(i) != c->constBegin()) + if (*(n = --i) == t) + return true; + n = c->end(); + return false; + } +#endif +}; template <class Key, class T> uint qHash(const QHash<Key, T> &key, uint seed = 0) diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 66770f6866..5d5da20752 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -847,9 +847,10 @@ void **QListData::erase(void **xi) /*! \fn template <class T> void QList<T>::insert(int i, const T &value) - Inserts \a value at index position \a i in the list. If \a i <= 0, - the value is prepended to the list. If \a i >= size(), the - value is appended to the list. + Inserts \a value at index position \a i in the list. + + If \a i == 0, the value is prepended to the list. If \a i == size(), + the value is appended to the list. Example: \snippet code/src_corelib_tools_qlistdata.cpp 8 diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 425ffa42a5..ffd470efcd 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -580,8 +580,16 @@ inline T &QList<T>::operator[](int i) detach(); return reinterpret_cast<Node *>(p.at(i))->t(); } template <typename T> inline void QList<T>::removeAt(int i) -{ if(i >= 0 && i < p.size()) { detach(); - node_destruct(reinterpret_cast<Node *>(p.at(i))); p.remove(i); } } +{ +#if !QT_DEPRECATED_SINCE(5, 15) + Q_ASSERT_X(i >= 0 && i < p.size(), "QList<T>::removeAt", "index out of range"); +#elif !defined(QT_NO_DEBUG) + if (i < 0 || i >= p.size()) + qWarning("QList::removeAt(): Index out of range."); +#endif + detach(); + node_destruct(reinterpret_cast<Node *>(p.at(i))); p.remove(i); +} template <typename T> inline T QList<T>::takeAt(int i) { Q_ASSERT_X(i >= 0 && i < p.size(), "QList<T>::take", "index out of range"); @@ -676,6 +684,12 @@ inline void QList<T>::prepend(const T &t) template <typename T> inline void QList<T>::insert(int i, const T &t) { +#if !QT_DEPRECATED_SINCE(5, 15) + Q_ASSERT_X(i >= 0 && i <= p.size(), "QList<T>::insert", "index out of range"); +#elif !defined(QT_NO_DEBUG) + if (i < 0 || i > p.size()) + qWarning("QList::insert(): Index out of range."); +#endif if (d->ref.isShared()) { Node *n = detach_helper_grow(i, 1); QT_TRY { diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index 2e72832185..879e2176f2 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -108,7 +108,11 @@ public: friend class QSet<T>; public: +#if QT_DEPRECATED_SINCE(5, 15) typedef std::bidirectional_iterator_tag iterator_category; +#else + typedef std::forward_iterator_tag iterator_category; +#endif typedef qptrdiff difference_type; typedef T value_type; typedef const T *pointer; @@ -128,13 +132,15 @@ public: { return i != o.i; } inline iterator &operator++() { ++i; return *this; } inline iterator operator++(int) { iterator r = *this; ++i; return r; } - inline iterator &operator--() { --i; return *this; } - inline iterator operator--(int) { iterator r = *this; --i; return r; } - inline iterator operator+(int j) const { return i + j; } - inline iterator operator-(int j) const { return i - j; } - friend inline iterator operator+(int j, iterator k) { return k + j; } - inline iterator &operator+=(int j) { i += j; return *this; } - inline iterator &operator-=(int j) { i -= j; return *this; } +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED iterator &operator--() { --i; return *this; } + inline QT_DEPRECATED iterator operator--(int) { iterator r = *this; --i; return r; } + inline QT_DEPRECATED iterator operator+(int j) const { return i + j; } + inline QT_DEPRECATED iterator operator-(int j) const { return i - j; } + friend inline QT_DEPRECATED iterator operator+(int j, iterator k) { return k + j; } + inline QT_DEPRECATED iterator &operator+=(int j) { i += j; return *this; } + inline QT_DEPRECATED iterator &operator-=(int j) { i -= j; return *this; } +#endif }; class const_iterator @@ -145,7 +151,11 @@ public: friend class QSet<T>; public: +#if QT_DEPRECATED_SINCE(5, 15) typedef std::bidirectional_iterator_tag iterator_category; +#else + typedef std::forward_iterator_tag iterator_category; +#endif typedef qptrdiff difference_type; typedef T value_type; typedef const T *pointer; @@ -163,19 +173,18 @@ public: inline bool operator!=(const const_iterator &o) const { return i != o.i; } inline const_iterator &operator++() { ++i; return *this; } inline const_iterator operator++(int) { const_iterator r = *this; ++i; return r; } - inline const_iterator &operator--() { --i; return *this; } - inline const_iterator operator--(int) { const_iterator r = *this; --i; return r; } - inline const_iterator operator+(int j) const { return i + j; } - inline const_iterator operator-(int j) const { return i - j; } - friend inline const_iterator operator+(int j, const_iterator k) { return k + j; } - inline const_iterator &operator+=(int j) { i += j; return *this; } - inline const_iterator &operator-=(int j) { i -= j; return *this; } +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED const_iterator &operator--() { --i; return *this; } + inline QT_DEPRECATED const_iterator operator--(int) { const_iterator r = *this; --i; return r; } + inline QT_DEPRECATED const_iterator operator+(int j) const { return i + j; } + inline QT_DEPRECATED const_iterator operator-(int j) const { return i - j; } + friend inline QT_DEPRECATED const_iterator operator+(int j, const_iterator k) { return k + j; } + inline QT_DEPRECATED const_iterator &operator+=(int j) { i += j; return *this; } + inline QT_DEPRECATED const_iterator &operator-=(int j) { i -= j; return *this; } +#endif }; // STL style - typedef std::reverse_iterator<iterator> reverse_iterator; - typedef std::reverse_iterator<const_iterator> const_reverse_iterator; - inline iterator begin() { return q_hash.begin(); } inline const_iterator begin() const noexcept { return q_hash.begin(); } inline const_iterator cbegin() const noexcept { return q_hash.begin(); } @@ -185,12 +194,17 @@ public: inline const_iterator cend() const noexcept { return q_hash.end(); } inline const_iterator constEnd() const noexcept { return q_hash.constEnd(); } - reverse_iterator rbegin() { return reverse_iterator(end()); } - reverse_iterator rend() { return reverse_iterator(begin()); } - const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } - const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } +#if QT_DEPRECATED_SINCE(5, 15) + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + + reverse_iterator QT_DEPRECATED rbegin() { return reverse_iterator(end()); } + reverse_iterator QT_DEPRECATED rend() { return reverse_iterator(begin()); } + const_reverse_iterator QT_DEPRECATED rbegin() const noexcept { return const_reverse_iterator(end()); } + const_reverse_iterator QT_DEPRECATED rend() const noexcept { return const_reverse_iterator(begin()); } + const_reverse_iterator QT_DEPRECATED crbegin() const noexcept { return const_reverse_iterator(end()); } + const_reverse_iterator QT_DEPRECATED crend() const noexcept { return const_reverse_iterator(begin()); } +#endif iterator erase(iterator i) { return erase(m2c(i)); } @@ -429,17 +443,19 @@ public: inline bool hasNext() const { return c->constEnd() != i; } inline const T &next() { n = i++; return *n; } inline const T &peekNext() const { return *i; } - inline bool hasPrevious() const { return c->constBegin() != i; } - inline const T &previous() { n = --i; return *n; } - inline const T &peekPrevious() const { iterator p = i; return *--p; } inline void remove() { if (c->constEnd() != n) { i = c->erase(n); n = c->end(); } } inline const T &value() const { Q_ASSERT(item_exists()); return *n; } inline bool findNext(const T &t) { while (c->constEnd() != (n = i)) if (*i++ == t) return true; return false; } - inline bool findPrevious(const T &t) +#if QT_DEPRECATED_SINCE(5, 15) + inline QT_DEPRECATED bool hasPrevious() const { return c->constBegin() != i; } + inline QT_DEPRECATED const T &previous() { n = --i; return *n; } + inline QT_DEPRECATED const T &peekPrevious() const { iterator p = i; return *--p; } + inline QT_DEPRECATED bool findPrevious(const T &t) { while (c->constBegin() != i) if (*(n = --i) == t) return true; n = c->end(); return false; } +#endif }; #endif // QT_NO_JAVA_STYLE_ITERATORS diff --git a/src/corelib/tools/qset.qdoc b/src/corelib/tools/qset.qdoc index 78854be0de..33a0697e12 100644 --- a/src/corelib/tools/qset.qdoc +++ b/src/corelib/tools/qset.qdoc @@ -906,8 +906,6 @@ Calling this function on QSet<T>::constEnd() leads to undefined results. - - \sa operator--() */ /*! @@ -924,6 +922,7 @@ /*! \fn template <class T> QSet<T>::iterator &QSet<T>::iterator::operator--() \fn template <class T> QSet<T>::const_iterator &QSet<T>::const_iterator::operator--() + \obsolete The prefix -- operator (\c{--it}) makes the preceding item current and returns an iterator to the new current item. @@ -937,6 +936,7 @@ /*! \fn template <class T> QSet<T>::iterator QSet<T>::iterator::operator--(int) \fn template <class T> QSet<T>::const_iterator QSet<T>::const_iterator::operator--(int) + \obsolete \overload @@ -947,6 +947,7 @@ /*! \fn template <class T> QSet<T>::iterator QSet<T>::iterator::operator+(int j) const \fn template <class T> QSet<T>::const_iterator QSet<T>::const_iterator::operator+(int j) const + \obsolete Returns an iterator to the item at \a j positions forward from this iterator. (If \a j is negative, the iterator goes backward.) @@ -959,6 +960,7 @@ /*! \fn template <class T> QSet<T>::iterator QSet<T>::iterator::operator-(int j) const \fn template <class T> QSet<T>::const_iterator QSet<T>::const_iterator::operator-(int j) const + \obsolete Returns an iterator to the item at \a j positions backward from this iterator. (If \a j is negative, the iterator goes forward.) @@ -971,6 +973,7 @@ /*! \fn template <class T> QSet<T>::iterator &QSet<T>::iterator::operator+=(int j) \fn template <class T> QSet<T>::const_iterator &QSet<T>::const_iterator::operator+=(int j) + \obsolete Advances the iterator by \a j items. (If \a j is negative, the iterator goes backward.) @@ -983,6 +986,7 @@ /*! \fn template <class T> QSet<T>::iterator &QSet<T>::iterator::operator-=(int j) \fn template <class T> QSet<T>::const_iterator &QSet<T>::const_iterator::operator-=(int j) + \obsolete Makes the iterator go back by \a j items. (If \a j is negative, the iterator goes forward.) diff --git a/src/corelib/tools/qsimd.cpp b/src/corelib/tools/qsimd.cpp index fb9b5996f6..d7c1d8c4a9 100644 --- a/src/corelib/tools/qsimd.cpp +++ b/src/corelib/tools/qsimd.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2019 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -188,6 +188,8 @@ static inline quint64 detectProcessorFeatures() # define PICreg "%%rbx" #endif +static bool checkRdrndWorks() noexcept; + static int maxBasicCpuidSupported() { #if defined(Q_CC_EMSCRIPTEN) @@ -376,37 +378,8 @@ static quint64 detectProcessorFeatures() features &= ~AllAVX512; } -#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND) - /** - * Some AMD CPUs (e.g. AMD A4-6250J and AMD Ryzen 3000-series) have a - * failing random generation instruction, which always returns - * 0xffffffff, even when generation was "successful". - * - * This code checks if hardware random generator generates four consecutive - * equal numbers. If it does, then we probably have a failing one and - * should disable it completely. - * - * https://bugreports.qt.io/browse/QTBUG-69423 - */ - if (features & CpuFeatureRDRND) { - const qsizetype testBufferSize = 4; - unsigned testBuffer[4] = {}; - - const qsizetype generated = qRandomCpu(testBuffer, testBufferSize); - - if (Q_UNLIKELY(generated == testBufferSize && - testBuffer[0] == testBuffer[1] && - testBuffer[1] == testBuffer[2] && - testBuffer[2] == testBuffer[3])) { - - fprintf(stderr, "WARNING: CPU random generator seem to be failing, disable hardware random number generation\n"); - fprintf(stderr, "WARNING: RDRND generated: 0x%x 0x%x 0x%x 0x%x\n", - testBuffer[0], testBuffer[1], testBuffer[2], testBuffer[3]); - - features &= ~CpuFeatureRDRND; - } - } -#endif + if (features & CpuFeatureRDRND && !checkRdrndWorks()) + features &= ~(CpuFeatureRDRND | CpuFeatureRDSEED); return features; } @@ -626,16 +599,45 @@ void qDumpCPUFeatures() # ifdef Q_PROCESSOR_X86_64 # define _rdrandXX_step _rdrand64_step +# define _rdseedXX_step _rdseed64_step # else # define _rdrandXX_step _rdrand32_step +# define _rdseedXX_step _rdseed32_step # endif -QT_FUNCTION_TARGET(RDRND) qsizetype qRandomCpu(void *buffer, qsizetype count) noexcept +# if QT_COMPILER_SUPPORTS_HERE(RDSEED) +static QT_FUNCTION_TARGET(RDSEED) unsigned *qt_random_rdseed(unsigned *ptr, unsigned *end) noexcept { - unsigned *ptr = reinterpret_cast<unsigned *>(buffer); - unsigned *end = ptr + count; - int retries = 10; + // Unlike for the RDRAND code below, the Intel whitepaper describing the + // use of the RDSEED instruction indicates we should not retry in a loop. + // If the independent bit generator used by RDSEED is out of entropy, it + // may take time to replenish. + // https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide + while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) { + if (_rdseedXX_step(reinterpret_cast<qregisteruint *>(ptr)) == 0) + goto out; + ptr += sizeof(qregisteruint)/sizeof(*ptr); + } + + if (sizeof(*ptr) != sizeof(qregisteruint) && ptr != end) { + if (_rdseed32_step(ptr) == 0) + goto out; + ++ptr; + } + +out: + return ptr; +} +# else +static unsigned *qt_random_rdseed(unsigned *ptr, unsigned *) +{ + return ptr; +} +# endif +static QT_FUNCTION_TARGET(RDRND) unsigned *qt_random_rdrnd(unsigned *ptr, unsigned *end) noexcept +{ + int retries = 10; while (ptr + sizeof(qregisteruint)/sizeof(*ptr) <= end) { if (_rdrandXX_step(reinterpret_cast<qregisteruint *>(ptr))) ptr += sizeof(qregisteruint)/sizeof(*ptr); @@ -653,9 +655,64 @@ QT_FUNCTION_TARGET(RDRND) qsizetype qRandomCpu(void *buffer, qsizetype count) no } out: - return ptr - reinterpret_cast<unsigned *>(buffer); + return ptr; +} + +static QT_FUNCTION_TARGET(RDRND) Q_DECL_COLD_FUNCTION bool checkRdrndWorks() noexcept +{ + /* + * Some AMD CPUs (e.g. AMD A4-6250J and AMD Ryzen 3000-series) have a + * failing random generation instruction, which always returns + * 0xffffffff, even when generation was "successful". + * + * This code checks if hardware random generator generates four consecutive + * equal numbers. If it does, then we probably have a failing one and + * should disable it completely. + * + * https://bugreports.qt.io/browse/QTBUG-69423 + */ + constexpr qsizetype TestBufferSize = 4; + unsigned testBuffer[TestBufferSize] = {}; + + unsigned *end = qt_random_rdrnd(testBuffer, testBuffer + TestBufferSize); + if (end < testBuffer + 3) { + // Random generation didn't produce enough data for us to make a + // determination whether it's working or not. Assume it isn't, but + // don't print a warning. + return false; + } + + // Check the results for equality + if (testBuffer[0] == testBuffer[1] + && testBuffer[0] == testBuffer[2] + && end == testBuffer + TestBufferSize && testBuffer[0] == testBuffer[3]) { + fprintf(stderr, "WARNING: CPU random generator seem to be failing, " + "disabling hardware random number generation\n" + "WARNING: RDRND generated:"); + for (unsigned *ptr = testBuffer; ptr < end; ++ptr) + fprintf(stderr, " 0x%x", *ptr); + fprintf(stderr, "\n"); + return false; + } + + // We're good + return true; } -#endif +QT_FUNCTION_TARGET(RDRND) qsizetype qRandomCpu(void *buffer, qsizetype count) noexcept +{ + unsigned *ptr = reinterpret_cast<unsigned *>(buffer); + unsigned *end = ptr + count; + + if (qCpuHasFeature(RDSEED)) + ptr = qt_random_rdseed(ptr, end); + + // fill the buffer with RDRND if RDSEED didn't + ptr = qt_random_rdrnd(ptr, end); + return ptr - reinterpret_cast<unsigned *>(buffer); +} +#elif defined(Q_PROCESSOR_X86) && !defined(Q_OS_NACL) && !defined(Q_PROCESSOR_ARM) +static bool checkRdrndWorks() noexcept { return false; } +#endif // Q_PROCESSOR_X86 && RDRND QT_END_NAMESPACE |