summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qrandom.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/global/qrandom.cpp')
-rw-r--r--src/corelib/global/qrandom.cpp299
1 files changed, 196 insertions, 103 deletions
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index 6a0b86dd6e..21448b8c14 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -1,60 +1,27 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 Intel Corporation.
+// 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
// for rand_s
#define _CRT_RAND_S
#include "qrandom.h"
#include "qrandom_p.h"
-#include <qobjectdefs.h>
+#include <qendian.h>
#include <qmutex.h>
-#include <qthreadstorage.h>
+#include <qobjectdefs.h>
#include <errno.h>
-#if !QT_CONFIG(getentropy) && (!defined(Q_OS_BSD4) || defined(__GLIBC__)) && !defined(Q_OS_WIN)
+#if QT_CONFIG(getauxval)
+# include <sys/auxv.h>
+#endif
+
+#if QT_CONFIG(getentropy) && __has_include(<sys/random.h>)
+# include <sys/random.h>
+#elif !QT_CONFIG(getentropy) && (!defined(Q_OS_BSD4) || defined(__GLIBC__)) && !defined(Q_OS_WIN)
# include "qdeadlinetimer.h"
# include "qhashfunctions.h"
-
-# if QT_CONFIG(getauxval)
-# include <sys/auxv.h>
-# endif
#endif // !QT_CONFIG(getentropy)
#ifdef Q_OS_UNIX
@@ -72,10 +39,6 @@ DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG Rando
}
#endif
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
-# include <private/qjni_p.h>
-#endif
-
// This file is too low-level for regular Q_ASSERT (the logging framework may
// recurse back), so use regular assert()
#undef NDEBUG
@@ -95,6 +58,10 @@ enum {
FillBufferNoexcept = true
};
+#if defined(QT_BUILD_INTERNAL)
+QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
+#endif
+
struct QRandomGenerator::SystemGenerator
{
#if QT_CONFIG(getentropy)
@@ -168,7 +135,7 @@ struct QRandomGenerator::SystemGenerator
}
#elif defined(Q_OS_WIN)
- qsizetype fillBuffer(void *buffer, qsizetype count) noexcept
+ static qsizetype fillBuffer(void *buffer, qsizetype count) noexcept
{
auto RtlGenRandom = SystemFunction036;
return RtlGenRandom(buffer, ULONG(count)) ? count: 0;
@@ -181,7 +148,8 @@ struct QRandomGenerator::SystemGenerator
// For std::mersenne_twister_engine implementations that use something
// other than quint32 (unsigned int) to fill their buffers.
- template <typename T> void generate(T *begin, T *end)
+ template<typename T>
+ void generate(T *begin, T *end)
{
static_assert(sizeof(T) >= sizeof(quint32));
if (sizeof(T) == sizeof(quint32)) {
@@ -225,10 +193,10 @@ static void fallback_fill(quint32 *ptr, qsizetype left) noexcept
arc4random_buf(ptr, left * sizeof(*ptr));
}
#else
-static QBasicAtomicInteger<unsigned> seed = Q_BASIC_ATOMIC_INITIALIZER(0U);
+Q_CONSTINIT static QBasicAtomicInteger<unsigned> seed = Q_BASIC_ATOMIC_INITIALIZER(0U);
static void fallback_update_seed(unsigned value)
{
- // Update the seed to be used for the fallback mechansim, if we need to.
+ // Update the seed to be used for the fallback mechanism, if we need to.
// We can't use QtPrivate::QHashCombine here because that is not an atomic
// operation. A simple XOR will have to do then.
seed.fetchAndXorRelaxed(value);
@@ -349,41 +317,34 @@ struct QRandomGenerator::SystemAndGlobalGenerators
// the state in case another thread tries to lock the mutex. It's not
// a common scenario, but since sizeof(QRandomGenerator) >= 2560, the
// overhead is actually acceptable.
- // 2) We use both alignas and std::aligned_storage<..., 64> because
- // some implementations of std::aligned_storage can't align to more
- // than a primitive type's alignment.
+ // 2) We use both alignas(T) and alignas(64) because some implementations
+ // can't align to more than a primitive type's alignment.
// 3) We don't store the entire system QRandomGenerator, only the space
// used by the QRandomGenerator::type member. This is fine because we
// (ab)use the common initial sequence exclusion to aliasing rules.
QBasicMutex globalPRNGMutex;
struct ShortenedSystem { uint type; } system_;
SystemGenerator sys;
- alignas(64) std::aligned_storage<sizeof(QRandomGenerator64), 64>::type global_;
+ alignas(64) struct {
+ alignas(QRandomGenerator64) uchar data[sizeof(QRandomGenerator64)];
+ } global_;
-#ifdef Q_COMPILER_CONSTEXPR
constexpr SystemAndGlobalGenerators()
: globalPRNGMutex{}, system_{0}, sys{}, global_{}
{}
-#endif
void confirmLiteral()
{
-#if defined(Q_COMPILER_CONSTEXPR) && !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY)
- // Currently fails to compile with MSVC 2017, saying QBasicMutex is not
- // a literal type. Disassembly with MSVC 2013 and 2015 shows it is
- // actually a literal; MSVC 2017 has a bug relating to this, so we're
- // withhold judgement for now. Integrity's compiler is unable to
- // guarantee g's alignment for some reason.
-
+#if !defined(Q_OS_INTEGRITY)
+ // Integrity's compiler is unable to guarantee g's alignment for some reason.
constexpr SystemAndGlobalGenerators g = {};
Q_UNUSED(g);
- static_assert(std::is_literal_type<SystemAndGlobalGenerators>::value);
#endif
}
static SystemAndGlobalGenerators *self()
{
- static SystemAndGlobalGenerators g;
+ Q_CONSTINIT static SystemAndGlobalGenerators g;
static_assert(sizeof(g) > sizeof(QRandomGenerator64));
return &g;
}
@@ -414,7 +375,8 @@ struct QRandomGenerator::SystemAndGlobalGenerators
new (&rng->storage.engine()) RandomEngine(self()->sys);
}
- struct PRNGLocker {
+ struct PRNGLocker
+ {
const bool locked;
PRNGLocker(const QRandomGenerator *that)
: locked(that == globalNoInit())
@@ -675,7 +637,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
position in the deterministic sequence as the \a other object was. Both
objects will generate the same sequence from this point on.
- For that reason, it is not adviseable to create a copy of
+ For that reason, it is not advisable to create a copy of
QRandomGenerator::global(). If one needs an exclusive deterministic
generator, consider instead using securelySeeded() to obtain a new object
that shares no relationship with the QRandomGenerator::global().
@@ -685,18 +647,17 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
\fn bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
\relates QRandomGenerator
- Returns true if the two the two engines \a rng1 and \a rng2 are at the same
+ Returns true if the two engines \a rng1 and \a rng2 are at the same
state or if they are both reading from the operating system facilities,
false otherwise.
*/
/*!
- \fn bool operator!=(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
- \relates QRandomGenerator
+ \fn bool QRandomGenerator::operator!=(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
- Returns true if the two the two engines \a rng1 and \a rng2 are at
+ Returns \c true if the two engines \a rng1 and \a rng2 are at
different states or if one of them is reading from the operating system
- facilities and the other is not, false otherwise.
+ facilities and the other is not, \c false otherwise.
*/
/*!
@@ -804,7 +765,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
*/
/*!
- \fn template <typename UInt> void QRandomGenerator::fillRange(UInt *buffer, qsizetype count)
+ \fn template <typename UInt, QRandomGenerator::IfValidUInt<UInt> = true> void QRandomGenerator::fillRange(UInt *buffer, qsizetype count)
Generates \a count 32- or 64-bit quantities (depending on the type \c UInt)
and stores them in the buffer pointed by \a buffer. This is the most
@@ -820,7 +781,7 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
*/
/*!
- \fn template <typename UInt, size_t N> void QRandomGenerator::fillRange(UInt (&buffer)[N])
+ \fn template <typename UInt, size_t N, QRandomGenerator::IfValidUInt<UInt> = true> void QRandomGenerator::fillRange(UInt (&buffer)[N])
Generates \c N 32- or 64-bit quantities (depending on the type \c UInt) and
stores them in the \a buffer array. This is the most efficient way to
@@ -876,7 +837,8 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
\a highest (exclusive). The same result may also be obtained by using
\l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{\c std::uniform_int_distribution}
with parameters 0 and \c{highest - 1}. That class can also be used to obtain
- quantities larger than 32 bits.
+ quantities larger than 32 bits; for 64 bits, the 64-bit bounded() overload
+ can be used too.
For example, to obtain a value between 0 and 255 (inclusive), one would write:
@@ -905,6 +867,45 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
*/
/*!
+ \fn quint64 QRandomGenerator::bounded(quint64 highest)
+ \overload
+
+ Generates one random 64-bit quantity in the range between 0 (inclusive) and
+ \a highest (exclusive). The same result may also be obtained by using
+ \l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{\c std::uniform_int_distribution<quint64>}
+ with parameters 0 and \c{highest - 1}.
+
+ Note that this function cannot be used to obtain values in the full 64-bit
+ range of \c{quint64}. Instead, use generate64().
+
+ \note This function is implemented as a loop, which depends on the random
+ value obtained. On the long run, on average it should loop just under 2
+ times, but if the random generator is defective, this function may take
+ considerably longer to execute.
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
+ \fn qint64 QRandomGenerator::bounded(qint64 highest)
+ \overload
+
+ Generates one random 64-bit quantity in the range between 0 (inclusive) and
+ \a highest (exclusive). \a highest must be positive.
+
+ Note that this function cannot be used to obtain values in the full 64-bit
+ range of \c{qint64}. Instead, use generate64() and cast to qint64 or instead
+ use the unsigned version of this function.
+
+ \note This function is implemented as a loop, which depends on the random
+ value obtained. On the long run, on average it should loop just under 2
+ times, but if the random generator is defective, this function may take
+ considerably longer to execute.
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
\fn quint32 QRandomGenerator::bounded(quint32 lowest, quint32 highest)
\overload
@@ -943,6 +944,60 @@ inline QRandomGenerator::SystemGenerator &QRandomGenerator::SystemGenerator::sel
*/
/*!
+ \fn quint64 QRandomGenerator::bounded(quint64 lowest, quint64 highest)
+ \overload
+
+ Generates one random 64-bit quantity in the range between \a lowest
+ (inclusive) and \a highest (exclusive). The \a highest parameter must be
+ greater than \a lowest.
+
+ The same result may also be obtained by using
+ \l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{\c std::uniform_int_distribution<quint64>}
+ with parameters \a lowest and \c{\a highest - 1}.
+
+ Note that this function cannot be used to obtain values in the full 64-bit
+ range of \c{quint64}. Instead, use generate64().
+
+ \note This function is implemented as a loop, which depends on the random
+ value obtained. On the long run, on average it should loop just under 2
+ times, but if the random generator is defective, this function may take
+ considerably longer to execute.
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
+ \fn qint64 QRandomGenerator::bounded(qint64 lowest, qint64 highest)
+ \overload
+
+ Generates one random 64-bit quantity in the range between \a lowest
+ (inclusive) and \a highest (exclusive), both of which may be negative, but
+ \a highest must be greater than \a lowest.
+
+ Note that this function cannot be used to obtain values in the full 64-bit
+ range of \c{qint64}. Instead, use generate64() and cast to qint64.
+
+ \note This function is implemented as a loop, which depends on the random
+ value obtained. On the long run, on average it should loop just under 2
+ times, but if the random generator is defective, this function may take
+ considerably longer to execute.
+
+ \sa generate(), generate64(), generateDouble()
+ */
+
+/*!
+ \fn qint64 QRandomGenerator::bounded(int lowest, qint64 highest)
+ \fn qint64 QRandomGenerator::bounded(qint64 lowest, int highest)
+ \fn quint64 QRandomGenerator::bounded(unsigned lowest, quint64 highest)
+ \fn quint64 QRandomGenerator::bounded(quint64 lowest, unsigned highest)
+ \overload
+
+ This function exists to help with overload resolution when the types of the
+ parameters don't exactly match. They will promote the smaller type to the
+ type of the larger one and call the correct overload.
+ */
+
+/*!
\fn QRandomGenerator *QRandomGenerator::system()
\threadsafe
@@ -1177,44 +1232,82 @@ bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
/*!
\internal
- Fills the range pointed by \a buffer and \a bufferEnd with 32-bit random
- values. The buffer must be correctly aligned.
+ Fills the range pointed by \a buffer with \a count 32-bit random values.
+ The buffer must be correctly aligned.
+
+ Returns the value of the first two 32-bit entries as a \c{quint64}.
*/
-void QRandomGenerator::_fillRange(void *buffer, void *bufferEnd)
+quint64 QRandomGenerator::_fillRange(void *buffer, qptrdiff count)
{
// Verify that the pointers are properly aligned for 32-bit
Q_ASSERT(quintptr(buffer) % sizeof(quint32) == 0);
- Q_ASSERT(quintptr(bufferEnd) % sizeof(quint32) == 0);
- quint32 *begin = static_cast<quint32 *>(buffer);
- quint32 *end = static_cast<quint32 *>(bufferEnd);
+ Q_ASSERT(count >= 0);
+ Q_ASSERT(buffer || count <= 2);
+
+ quint64 dummy;
+ quint32 *begin = static_cast<quint32 *>(buffer ? buffer : &dummy);
+ quint32 *end = begin + count;
+
+ if (type == SystemRNG || Q_UNLIKELY(uint(qt_randomdevice_control.loadAcquire()) & (UseSystemRNG|SetRandomData))) {
+ SystemGenerator::self().generate(begin, end);
+ } else {
+ SystemAndGlobalGenerators::PRNGLocker lock(this);
+ std::generate(begin, end, [this]() { return storage.engine()(); });
+ }
- if (type == SystemRNG || Q_UNLIKELY(uint(qt_randomdevice_control.loadAcquire()) & (UseSystemRNG|SetRandomData)))
- return SystemGenerator::self().generate(begin, end);
+ if (end - begin == 1)
+ return *begin;
+ return begin[0] | (quint64(begin[1]) << 32);
+}
- SystemAndGlobalGenerators::PRNGLocker lock(this);
- std::generate(begin, end, [this]() { return storage.engine()(); });
+// helper function to call fillBuffer, since we need something to be
+// argument-dependent
+template <typename Generator, typename FillBufferType, typename T>
+static qsizetype callFillBuffer(FillBufferType f, T *v)
+{
+ if constexpr (std::is_member_function_pointer_v<FillBufferType>) {
+ // member function, need an object
+ return (Generator::self().*f)(v, sizeof(*v));
+ } else {
+ // static, call directly
+ return f(v, sizeof(*v));
+ }
}
-namespace {
-struct QRandEngine
+/*!
+ \internal
+
+ Returns an initial random value (useful for QHash's global seed). This
+ function attempts to use OS-provided random values to avoid initializing
+ QRandomGenerator::system() and qsimd.cpp.
+
+ Note: on some systems, this functionn may rerturn the same value every time
+ it is called.
+ */
+QRandomGenerator::InitialRandomData qt_initial_random_value() noexcept
{
- std::minstd_rand engine;
- QRandEngine() : engine(1) {}
+#if QT_CONFIG(getauxval) && defined(AT_RANDOM)
+ auto at_random_ptr = reinterpret_cast<size_t *>(getauxval(AT_RANDOM));
+ if (at_random_ptr)
+ return qFromUnaligned<QRandomGenerator::InitialRandomData>(at_random_ptr);
+#endif
- int generate()
- {
- std::minstd_rand::result_type v = engine();
- if (std::numeric_limits<int>::max() != RAND_MAX)
- v %= uint(RAND_MAX) + 1;
+ // bypass the hardware RNG, which would mean initializing qsimd.cpp
- return int(v);
- }
+ QRandomGenerator::InitialRandomData v;
+ for (int attempts = 16; attempts; --attempts) {
+ using Generator = QRandomGenerator::SystemGenerator;
+ auto fillBuffer = &Generator::fillBuffer;
+ if (callFillBuffer<Generator>(fillBuffer, &v) != sizeof(v))
+ continue;
- void seed(std::minstd_rand::result_type q)
- {
- engine.seed(q);
+ return v;
}
-};
+
+ quint32 data[sizeof(v) / sizeof(quint32)];
+ fallback_fill(data, std::size(data));
+ memcpy(v.data, data, sizeof(v.data));
+ return v;
}
QT_END_NAMESPACE