Change QRandomGenerator to have a deterministic mode
Now only QRandomGenerator::system() will access the system-wide RNG, which we document to be cryptographically-safe and possibly backed by a true HWRNG. Everything else just wraps a Mersenne Twister. Change-Id: I0a103569c81b4711a649fffd14ec8cd3469425df Reviewed-by: Lars Knoll <lars.knoll@qt.io>bb10
parent
4502999ff0
commit
af456842e1
|
|
@ -43,10 +43,8 @@
|
|||
#include "qrandom.h"
|
||||
#include "qrandom_p.h"
|
||||
#include <qobjectdefs.h>
|
||||
#include <qmutex.h>
|
||||
#include <qthreadstorage.h>
|
||||
#include <private/qsimd_p.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
|
@ -86,6 +84,7 @@ DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG Rando
|
|||
#undef Q_ASSERT_X
|
||||
#undef Q_ASSERT
|
||||
#define Q_ASSERT(cond) assert(cond)
|
||||
#define Q_ASSERT_X(cond, x, msg) assert(cond && msg)
|
||||
#if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS)
|
||||
# define NDEBUG 1
|
||||
#endif
|
||||
|
|
@ -122,13 +121,41 @@ static QT_FUNCTION_TARGET(RDRND) qssize_t qt_random_cpu(void *buffer, qssize_t c
|
|||
out:
|
||||
return ptr - reinterpret_cast<unsigned *>(buffer);
|
||||
}
|
||||
#else
|
||||
static qssize_t qt_random_cpu(void *, qssize_t)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#if QT_CONFIG(getentropy)
|
||||
class SystemRandom
|
||||
static QBasicMutex globalPRNGMutex;
|
||||
|
||||
struct PRNGLocker
|
||||
{
|
||||
public:
|
||||
const bool locked;
|
||||
PRNGLocker(const QRandomGenerator *that)
|
||||
: locked(that == nullptr || that == QRandomGenerator::global())
|
||||
{
|
||||
if (locked)
|
||||
globalPRNGMutex.lock();
|
||||
}
|
||||
~PRNGLocker()
|
||||
{
|
||||
if (locked)
|
||||
globalPRNGMutex.unlock();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
enum {
|
||||
// may be "overridden" by a member enum
|
||||
FillBufferNoexcept = true
|
||||
};
|
||||
|
||||
struct QRandomGenerator::SystemGenerator : public QRandomGenerator::SystemGeneratorBase
|
||||
{
|
||||
#if QT_CONFIG(getentropy)
|
||||
static qssize_t fillBuffer(void *buffer, qssize_t count) Q_DECL_NOTHROW
|
||||
{
|
||||
// getentropy can read at most 256 bytes, so break the reading
|
||||
|
|
@ -146,94 +173,94 @@ public:
|
|||
Q_UNUSED(ret);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
#elif defined(Q_OS_UNIX)
|
||||
class SystemRandom
|
||||
{
|
||||
static QBasicAtomicInt s_fdp1; // "file descriptor plus 1"
|
||||
static int openDevice();
|
||||
QBasicAtomicInt fdp1; // "file descriptor plus 1"
|
||||
int openDevice()
|
||||
{
|
||||
int fd = fdp1.loadAcquire() - 1;
|
||||
if (fd != -1)
|
||||
return fd;
|
||||
|
||||
fd = qt_safe_open("/dev/urandom", O_RDONLY);
|
||||
if (fd == -1)
|
||||
fd = qt_safe_open("/dev/random", O_RDONLY | O_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
// failed on both, set to -2 so we won't try again
|
||||
fd = -2;
|
||||
}
|
||||
|
||||
int opened_fdp1;
|
||||
if (fdp1.testAndSetOrdered(0, fd + 1, opened_fdp1))
|
||||
return fd;
|
||||
|
||||
// failed, another thread has opened the file descriptor
|
||||
if (fd >= 0)
|
||||
qt_safe_close(fd);
|
||||
return opened_fdp1 - 1;
|
||||
}
|
||||
|
||||
#ifdef Q_CC_GNU
|
||||
// If it's not GCC or GCC-like, then we'll leak the file descriptor
|
||||
__attribute__((destructor))
|
||||
#endif
|
||||
static void closeDevice();
|
||||
SystemRandom() {}
|
||||
public:
|
||||
enum { EfficientBufferFill = true };
|
||||
static qssize_t fillBuffer(void *buffer, qssize_t count);
|
||||
};
|
||||
QBasicAtomicInt SystemRandom::s_fdp1 = Q_BASIC_ATOMIC_INITIALIZER(0);
|
||||
|
||||
void SystemRandom::closeDevice()
|
||||
{
|
||||
int fd = s_fdp1.loadAcquire() - 1;
|
||||
if (fd >= 0)
|
||||
qt_safe_close(fd);
|
||||
}
|
||||
|
||||
int SystemRandom::openDevice()
|
||||
{
|
||||
int fd = s_fdp1.loadAcquire() - 1;
|
||||
if (fd != -1)
|
||||
return fd;
|
||||
|
||||
fd = qt_safe_open("/dev/urandom", O_RDONLY);
|
||||
if (fd == -1)
|
||||
fd = qt_safe_open("/dev/random", O_RDONLY | O_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
// failed on both, set to -2 so we won't try again
|
||||
fd = -2;
|
||||
static void closeDevice()
|
||||
{
|
||||
int fd = static_cast<SystemGenerator &>(system()->storage.sys).fdp1.load() - 1;
|
||||
if (fd >= 0)
|
||||
qt_safe_close(fd);
|
||||
}
|
||||
|
||||
int opened_fdp1;
|
||||
if (s_fdp1.testAndSetOrdered(0, fd + 1, opened_fdp1)) {
|
||||
if (fd >= 0) {
|
||||
static const SystemRandom closer;
|
||||
Q_UNUSED(closer);
|
||||
}
|
||||
return fd;
|
||||
SystemGenerator() : fdp1 Q_BASIC_ATOMIC_INITIALIZER(0) {}
|
||||
|
||||
qssize_t fillBuffer(void *buffer, qssize_t count)
|
||||
{
|
||||
int fd = openDevice();
|
||||
if (Q_UNLIKELY(fd < 0))
|
||||
return 0;
|
||||
|
||||
qint64 n = qt_safe_read(fd, buffer, count);
|
||||
return qMax<qssize_t>(n, 0); // ignore any errors
|
||||
}
|
||||
|
||||
// failed, another thread has opened the file descriptor
|
||||
if (fd >= 0)
|
||||
qt_safe_close(fd);
|
||||
return opened_fdp1 - 1;
|
||||
}
|
||||
|
||||
qssize_t SystemRandom::fillBuffer(void *buffer, qssize_t count)
|
||||
{
|
||||
int fd = openDevice();
|
||||
if (Q_UNLIKELY(fd < 0))
|
||||
return 0;
|
||||
|
||||
qint64 n = qt_safe_read(fd, buffer, count);
|
||||
return qMax<qssize_t>(n, 0); // ignore any errors
|
||||
}
|
||||
#endif // Q_OS_UNIX
|
||||
|
||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
||||
class SystemRandom
|
||||
{
|
||||
public:
|
||||
static qssize_t fillBuffer(void *buffer, qssize_t count) Q_DECL_NOTHROW
|
||||
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
|
||||
qssize_t fillBuffer(void *buffer, qssize_t count) Q_DECL_NOTHROW
|
||||
{
|
||||
auto RtlGenRandom = SystemFunction036;
|
||||
return RtlGenRandom(buffer, ULONG(count)) ? count: 0;
|
||||
}
|
||||
};
|
||||
#elif defined(Q_OS_WINRT)
|
||||
class SystemRandom
|
||||
{
|
||||
public:
|
||||
static qssize_t fillBuffer(void *, qssize_t) Q_DECL_NOTHROW
|
||||
qssize_t fillBuffer(void *, qssize_t) Q_DECL_NOTHROW
|
||||
{
|
||||
// always use the fallback
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
#endif // Q_OS_WINRT
|
||||
} // unnamed namespace
|
||||
|
||||
static SystemGenerator &self()
|
||||
{
|
||||
return static_cast<SystemGenerator &>(QRandomGenerator::system()->storage.sys);
|
||||
}
|
||||
void generate(quint32 *begin, quint32 *end) Q_DECL_NOEXCEPT_EXPR(FillBufferNoexcept);
|
||||
|
||||
// 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)
|
||||
{
|
||||
Q_STATIC_ASSERT(sizeof(T) >= sizeof(quint32));
|
||||
if (sizeof(T) == sizeof(quint32)) {
|
||||
// Microsoft Visual Studio uses unsigned long, but that's still 32-bit
|
||||
generate(reinterpret_cast<quint32 *>(begin), reinterpret_cast<quint32 *>(end));
|
||||
} else {
|
||||
// Slow path. Fix your C++ library.
|
||||
std::generate(begin, end, [this]() {
|
||||
quint32 datum;
|
||||
generate(&datum, &datum + 1);
|
||||
return datum;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
static void fallback_update_seed(unsigned) {}
|
||||
|
|
@ -252,6 +279,7 @@ static void fallback_update_seed(unsigned) {}
|
|||
static void fallback_fill(quint32 *, qssize_t) Q_DECL_NOTHROW
|
||||
{
|
||||
// no fallback necessary, getentropy cannot fail under normal circumstances
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
#elif defined(Q_OS_BSD4)
|
||||
static void fallback_update_seed(unsigned) {}
|
||||
|
|
@ -347,26 +375,11 @@ static void fallback_fill(quint32 *ptr, qssize_t left) Q_DECL_NOTHROW
|
|||
}
|
||||
#endif
|
||||
|
||||
static qssize_t fill_cpu(quint32 *buffer, qssize_t count) Q_DECL_NOTHROW
|
||||
Q_NEVER_INLINE void QRandomGenerator::SystemGenerator::generate(quint32 *begin, quint32 *end)
|
||||
Q_DECL_NOEXCEPT_EXPR(FillBufferNoexcept)
|
||||
{
|
||||
#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
|
||||
if (qCpuHasFeature(RDRND) && (uint(qt_randomdevice_control) & SkipHWRNG) == 0)
|
||||
return qt_random_cpu(buffer, count);
|
||||
#else
|
||||
Q_UNUSED(buffer);
|
||||
Q_UNUSED(count);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static Q_NEVER_INLINE void fill(void *begin, void *end)
|
||||
Q_DECL_NOEXCEPT_EXPR(noexcept(SystemRandom::fillBuffer(nullptr, 1)))
|
||||
{
|
||||
// Verify that the pointers are properly aligned for 32-bit
|
||||
Q_ASSERT(quintptr(begin) % sizeof(quint32) == 0);
|
||||
Q_ASSERT(quintptr(end) % sizeof(quint32) == 0);
|
||||
quint32 *buffer = reinterpret_cast<quint32 *>(begin);
|
||||
qssize_t count = reinterpret_cast<quint32 *>(end) - buffer;
|
||||
quint32 *buffer = begin;
|
||||
qssize_t count = end - begin;
|
||||
|
||||
if (Q_UNLIKELY(uint(qt_randomdevice_control) & SetRandomData)) {
|
||||
uint value = uint(qt_randomdevice_control) & RandomDataMask;
|
||||
|
|
@ -374,10 +387,13 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
return;
|
||||
}
|
||||
|
||||
qssize_t filled = fill_cpu(buffer, count);
|
||||
qssize_t filled = 0;
|
||||
if (qt_has_hwrng() && (uint(qt_randomdevice_control) & SkipHWRNG) == 0)
|
||||
filled += qt_random_cpu(buffer, count);
|
||||
|
||||
if (filled != count && (uint(qt_randomdevice_control) & SkipSystemRNG) == 0) {
|
||||
qssize_t bytesFilled =
|
||||
SystemRandom::fillBuffer(buffer + filled, (count - filled) * qssize_t(sizeof(*buffer)));
|
||||
fillBuffer(buffer + filled, (count - filled) * qssize_t(sizeof(*buffer)));
|
||||
filled += bytesFilled / qssize_t(sizeof(*buffer));
|
||||
}
|
||||
if (filled)
|
||||
|
|
@ -392,85 +408,153 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
/*!
|
||||
\class QRandomGenerator
|
||||
\inmodule QtCore
|
||||
\reentrant
|
||||
\since 5.10
|
||||
|
||||
\brief The QRandomGenerator class allows one to obtain random values from a
|
||||
high-quality, seed-less Random Number Generator.
|
||||
high-quality Random Number Generator.
|
||||
|
||||
QRandomGenerator may be used to generate random values from a high-quality
|
||||
random number generator. Unlike qrand(), QRandomGenerator does not need to be
|
||||
seeded. That also means it is not possible to force it to produce a
|
||||
reliable sequence, which may be needed for debugging.
|
||||
random number generator. Like the C++ random engines, QRandomGenerator can
|
||||
be seeded with user-provided values through the constructor.
|
||||
When seeded, the sequence of numbers generated by this
|
||||
class is deterministic. That is to say, given the same seed data,
|
||||
QRandomGenerator will generate the same sequence of numbers. But given
|
||||
different seeds, the results should be considerably different.
|
||||
|
||||
QRandomGenerator::global() returns a global instance of QRandomGenerator
|
||||
that Qt will ensure to be securely seeded. This object is thread-safe, may
|
||||
be shared for most uses, and is always seeded from
|
||||
QRandomGenerator::system()
|
||||
|
||||
QRandomGenerator::system() may be used to access the system's
|
||||
cryptographically-safe random generator. On Unix systems, it's equivalent
|
||||
to reading from \c {/dev/urandom} or the \c {getrandom()} or \c
|
||||
{getentropy()} system calls.
|
||||
|
||||
The class can generate 32-bit or 64-bit quantities, or fill an array of
|
||||
those. The most common way of generating new values is to call the generate(),
|
||||
generate64() or fillRange() functions. One would use it as:
|
||||
|
||||
\code
|
||||
quint32 value = QRandomGenerator::generate();
|
||||
quint32 value = QRandomGenerator::global()->generate();
|
||||
\endcode
|
||||
|
||||
Additionally, it provides a floating-point function generateDouble() that returns
|
||||
a number in the range [0, 1) (that is, inclusive of zero and exclusive of
|
||||
1). There's also a set of convenience functions that facilitate obtaining a
|
||||
random number in a bounded, integral range.
|
||||
Additionally, it provides a floating-point function generateDouble() that
|
||||
returns a number in the range [0, 1) (that is, inclusive of zero and
|
||||
exclusive of 1). There's also a set of convenience functions that
|
||||
facilitate obtaining a random number in a bounded, integral range.
|
||||
|
||||
\warning This class is not suitable for bulk data creation. See below for the
|
||||
technical reasons.
|
||||
\section1 Seeding and determinism
|
||||
|
||||
\section1 Frequency and entropy exhaustion
|
||||
QRandomGenerator may be seeded with specific seed data. When that is done,
|
||||
the numbers generated by the object will always be the same, as in the
|
||||
following example:
|
||||
|
||||
QRandomGenerator does not need to be seeded and instead uses operating system
|
||||
or hardware facilities to generate random numbers. On some systems and with
|
||||
certain hardware, those facilities are true Random Number Generators.
|
||||
However, if they are true RNGs, those facilities have finite entropy source
|
||||
and thus may fail to produce any results if the entropy pool is exhausted.
|
||||
\code
|
||||
QRandomGenerator prng1(1234), prng2(1234);
|
||||
Q_ASSERT(prng1.generate32() == prng2.generate32());
|
||||
Q_ASSERT(prng1.generate64() == prng2.generate64());
|
||||
\endcode
|
||||
|
||||
The seed data takes the form of one or more 32-bit words. The ideal seed
|
||||
size is approximately equal to the size of the QRandomGenerator class
|
||||
itself. Due to mixing of the seed data, QRandomGenerator cannot guarantee
|
||||
that distinct seeds will produce different sequences.
|
||||
|
||||
QRandomGenerator::global() is always seeded from
|
||||
QRandomGenerator::system(), so it's not possible to make it produce
|
||||
identical sequences.
|
||||
|
||||
\section1 Bulk data
|
||||
|
||||
When operating in deterministic mode, QRandomGenerator may be used for bulk
|
||||
data generation. In fact, applications that do not need
|
||||
cryptographically-secure or true random data are advised to use a regular
|
||||
QRandomGenerator instead of QRandomGenerator::system() for their random
|
||||
data needs.
|
||||
|
||||
For ease of use, QRandomGenerator provides a global object that can
|
||||
be easily used, as in the following example:
|
||||
|
||||
\code
|
||||
int x = QRandomGenerator::global()->generate32();
|
||||
int y = QRandomGenerator::global()->generate32();
|
||||
int w = QRandomGenerator::global()->bounded(16384);
|
||||
int h = QRandomGenerator::global()->bounded(16384);
|
||||
\endcode
|
||||
|
||||
\section1 System-wide random number generator
|
||||
|
||||
QRandomGenerator::system() may be used to access the system-wide random
|
||||
number generator, which is cryptographically-safe on all systems that Qt
|
||||
runs on. This function will use hardware facilities to generate random
|
||||
numbers where available. On such systems, those facilities are true Random
|
||||
Number Generators. However, if they are true RNGs, those facilities have
|
||||
finite entropy sources and thus may fail to produce any results if their
|
||||
entropy pool is exhausted.
|
||||
|
||||
If that happens, first the operating system then QRandomGenerator will fall
|
||||
back to Pseudo Random Number Generators of decreasing qualities (Qt's
|
||||
fallback generator being the simplest). Therefore, QRandomGenerator should
|
||||
not be used for high-frequency random number generation, lest the entropy
|
||||
pool become empty. As a rule of thumb, this class should not be called upon
|
||||
to generate more than a kilobyte per second of random data (note: this may
|
||||
vary from system to system).
|
||||
fallback generator being the simplest). Whether those generators are still
|
||||
of cryptographic quality is implementation-defined. Therefore,
|
||||
QRandomGenerator::system() should not be used for high-frequency random
|
||||
number generation, lest the entropy pool become empty. As a rule of thumb,
|
||||
this class should not be called upon to generate more than a kilobyte per
|
||||
second of random data (note: this may vary from system to system).
|
||||
|
||||
If an application needs true RNG data in bulk, it should use the operating
|
||||
system facilities (such as \c{/dev/random} on Unix systems) directly and
|
||||
wait for entropy to become available. If true RNG is not required,
|
||||
applications should instead use a PRNG engines and can use QRandomGenerator to
|
||||
seed those.
|
||||
system facilities (such as \c{/dev/random} on Linux) directly and wait for
|
||||
entropy to become available. If the application requires PRNG engines of
|
||||
cryptographic quality but not of true randomness,
|
||||
QRandomGenerator::system() may still be used (see section below).
|
||||
|
||||
If neither a true RNG nor a cryptographically secure PRNG are required,
|
||||
applications should instead use PRNG engines like QRandomGenerator's
|
||||
deterministic mode and those from the C++ Standard Library.
|
||||
QRandomGenerator::system() can be used to seed those.
|
||||
|
||||
\section2 Fallback quality
|
||||
|
||||
QRandomGenerator::system() uses the operating system facilities to obtain
|
||||
random numbers, which attempt to collect real entropy from the surrounding
|
||||
environment to produce true random numbers. However, it's possible that the
|
||||
entropy pool becomes exhausted, in which case the operating system will
|
||||
fall back to a pseudo-random engine for a time. Under no circumstances will
|
||||
QRandomGenerator::system() block, waiting for more entropy to be collected.
|
||||
|
||||
The following operating systems guarantee that the results from their
|
||||
random-generation API will be of at least cryptographically-safe quality,
|
||||
even if the entropy pool is exhausted: Apple OSes (Darwin), BSDs, Linux,
|
||||
Windows. Barring a system installation problem (such as \c{/dev/urandom}
|
||||
not being readable by the current process), QRandomGenerator::system() will
|
||||
therefore have the same guarantees.
|
||||
|
||||
On other operating systems, QRandomGenerator will fall back to a PRNG of
|
||||
good numeric distribution, but it cannot guarantee proper seeding in all
|
||||
cases. Please consult the OS documentation for more information.
|
||||
|
||||
Applications that require QRandomGenerator not to fall back to
|
||||
non-cryptographic quality generators are advised to check their operating
|
||||
system documentation or restrict their deployment to one of the above.
|
||||
|
||||
\section1 Reentrancy and thread-safety
|
||||
|
||||
QRandomGenerator is reentrant, meaning that multiple threads can operate on
|
||||
this class at the same time, so long as they operate on different objects.
|
||||
If multiple threads need to share one PRNG sequence, external locking by a
|
||||
mutex is required.
|
||||
|
||||
The exceptions are the objects returned by QRandomGenerator::global() and
|
||||
QRandomGenerator::system(): those objects are thread-safe and may be used
|
||||
by any thread without external locking. Note that thread-safety does not
|
||||
extend to copying those objects: they should always be used by reference.
|
||||
|
||||
\section1 Standard C++ Library compatibility
|
||||
|
||||
QRandomGenerator is modeled after
|
||||
\c{\l{http://en.cppreference.com/w/cpp/numeric/random/random_device}{std::random_device}}
|
||||
and may be used in almost all contexts that the Standard Library can.
|
||||
QRandomGenerator attempts to use either the same engine that backs
|
||||
\c{std::random_device} or a better one. Note that \c{std::random_device} is
|
||||
also allowed to fail if the source entropy pool becomes exhausted, in which
|
||||
case it will throw an exception. QRandomGenerator never throws, but may abort
|
||||
program execution instead.
|
||||
|
||||
Like the Standard Library class, QRandomGenerator can be used to seed Standard
|
||||
Library deterministic random engines from \c{<random>}, such as the
|
||||
Mersenne Twister. Unlike \c{std::random_device}, QRandomGenerator also
|
||||
implements the API of
|
||||
\c{\l{http://en.cppreference.com/w/cpp/numeric/random/seed_seq}{std::seed_seq}},
|
||||
allowing it to seed the deterministic engines directly.
|
||||
|
||||
The following code can be used to create and seed the
|
||||
implementation-defined default deterministic PRNG, then use it to fill a
|
||||
block range:
|
||||
|
||||
\code
|
||||
QRandomGenerator rd;
|
||||
std::default_random_engine rng(rd);
|
||||
std::generate(block.begin(), block.end(), rng);
|
||||
|
||||
// equivalent to:
|
||||
for (auto &v : block)
|
||||
v = rng();
|
||||
\endcode
|
||||
QRandomGenerator is modeled after the requirements for random number
|
||||
engines in the C++ Standard Library and may be used in almost all contexts
|
||||
that the Standard Library engines can.
|
||||
|
||||
QRandomGenerator is also compatible with the uniform distribution classes
|
||||
\c{std::uniform_int_distribution} and \c{std:uniform_real_distribution}, as
|
||||
|
|
@ -479,24 +563,85 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
[1, 2.5):
|
||||
|
||||
\code
|
||||
QRandomGenerator64 rd;
|
||||
std::uniform_real_distribution dist(1, 2.5);
|
||||
return dist(rd);
|
||||
return dist(*QRandomGenerator::global());
|
||||
\endcode
|
||||
|
||||
Note the use of the QRandomGenerator64 class instead of QRandomGenerator to
|
||||
obtain 64 bits of random data in a single call, though it is not required
|
||||
to make the algorithm work (the Standard Library functions will make as
|
||||
many calls as required to obtain enough bits of random data for the desired
|
||||
range).
|
||||
|
||||
\sa QRandomGenerator64, qrand()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator::QRandomGenerator()
|
||||
\internal
|
||||
Defaulted constructor, does nothing.
|
||||
\fn QRandomGenerator::QRandomGenerator(quint32 seed)
|
||||
|
||||
Initializes this QRandomGenerator object with the value \a seed as
|
||||
the seed. Two objects constructed with the same seed value will
|
||||
produce the same number sequence.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator::QRandomGenerator(const quint32 (&seedBuffer)[N])
|
||||
\overload
|
||||
|
||||
Initializes this QRandomGenerator object with the values found in the
|
||||
array \a seedBuffer as the seed. Two objects constructed or reseeded with
|
||||
the same seed value will produce the same number sequence.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator::QRandomGenerator(const quint32 *seedBuffer, qssize_t len)
|
||||
\overload
|
||||
|
||||
Initializes this QRandomGenerator object with \a len values found in
|
||||
the array \a seedBuffer as the seed. Two objects constructed or reseeded
|
||||
with the same seed value will produce the same number sequence.
|
||||
|
||||
This constructor is equivalent to:
|
||||
\code
|
||||
std::seed_seq sseq(seedBuffer, seedBuffer + len);
|
||||
QRandomGenerator generator(sseq);
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator::QRandomGenerator(const quint32 *begin, const quin32 *end)
|
||||
\overload
|
||||
|
||||
Initializes this QRandomGenerator object with the values found in the range
|
||||
from \a begin to \a end as the seed. Two objects constructed or reseeded
|
||||
with the same seed value will produce the same number sequence.
|
||||
|
||||
This constructor is equivalent to:
|
||||
\code
|
||||
std::seed_seq sseq(begin, end);
|
||||
QRandomGenerator generator(sseq);
|
||||
\endcode
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator::QRandomGenerator(std::seed_seq &sseq)
|
||||
\overload
|
||||
|
||||
Initializes this QRandomGenerator object with the seed sequence \a
|
||||
sseq as the seed. Two objects constructed or reseeded with the same seed
|
||||
value will produce the same number sequence.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator::QRandomGenerator(const QRandomGenerator &other)
|
||||
|
||||
Creates a copy of the generator state in the \a other object. If \a other is
|
||||
QRandomGenerator::system() or a copy of that, this object will also read
|
||||
from the operating system random-generating facilities. In that case, the
|
||||
sequences generated by the two objects will be different.
|
||||
|
||||
In all other cases, the new QRandomGenerator object will start at the same
|
||||
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
|
||||
QRandomGenerator::global(). If one needs an exclusive deterministic
|
||||
generator, consider instead creating a new object and seeding it from
|
||||
QRandomGenerator::system().
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
|
@ -512,24 +657,7 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
|
||||
Generates a 32-bit random quantity and returns it.
|
||||
|
||||
\sa QRandomGenerator::generate(), QRandomGenerator::generate64()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn double QRandomGenerator::entropy() const
|
||||
|
||||
Returns the estimate of the entropy in the random generator source.
|
||||
|
||||
This function exists to comply with the Standard Library requirements for
|
||||
\c{\l{http://en.cppreference.com/w/cpp/numeric/random/random_device}{std::random_device}}
|
||||
but it does not and cannot ever work. It is not possible to obtain a
|
||||
reliable entropy value in a shared entropy pool in a multi-tasking system,
|
||||
as other processes or threads may use that entropy. Any value non-zero
|
||||
value that this function could return would be obsolete by the time the
|
||||
user code reached it.
|
||||
|
||||
Since QRandomGenerator attempts to use a hardware Random Number Generator,
|
||||
this function always returns 0.0.
|
||||
\sa generate(), generate64()
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
|
@ -537,7 +665,7 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
|
||||
Returns the minimum value that QRandomGenerator may ever generate. That is, 0.
|
||||
|
||||
\sa max(), QRandomGenerator64::max()
|
||||
\sa max(), QRandomGenerator64::min()
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
|
@ -556,7 +684,7 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
and \a end. This function is equivalent to (and is implemented as):
|
||||
|
||||
\code
|
||||
std::generate(begin, end, []() { return generate(); });
|
||||
std::generate(begin, end, [this]() { return generate(); });
|
||||
\endcode
|
||||
|
||||
This function complies with the requirements for the function
|
||||
|
|
@ -569,7 +697,7 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
quantities, one can write:
|
||||
|
||||
\code
|
||||
std::generate(begin, end, []() { return QRandomGenerator::generate64(); });
|
||||
std::generate(begin, end, []() { return QRandomGenerator::global()->generate64(); });
|
||||
\endcode
|
||||
|
||||
If the range refers to contiguous memory (such as an array or the data from
|
||||
|
|
@ -647,26 +775,26 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
*/
|
||||
|
||||
/*!
|
||||
\fn qreal QRandomGenerator::bounded(qreal sup)
|
||||
\fn qreal QRandomGenerator::bounded(qreal highest)
|
||||
|
||||
Generates one random qreal in the range between 0 (inclusive) and \a
|
||||
sup (exclusive). This function is equivalent to and is implemented as:
|
||||
highest (exclusive). This function is equivalent to and is implemented as:
|
||||
|
||||
\code
|
||||
return generateDouble() * sup;
|
||||
return generateDouble() * highest;
|
||||
\endcode
|
||||
|
||||
\sa generateDouble(), bounded()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn quint32 QRandomGenerator::bounded(quint32 sup)
|
||||
\fn quint32 QRandomGenerator::bounded(quint32 highest)
|
||||
\overload
|
||||
|
||||
Generates one random 32-bit quantity in the range between 0 (inclusive) and
|
||||
\a sup (exclusive). The same result may also be obtained by using
|
||||
\a highest (exclusive). The same result may also be obtained by using
|
||||
\c{\l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{std::uniform_int_distribution}}
|
||||
with parameters 0 and \c{sup - 1}. That class can also be used to obtain
|
||||
with parameters 0 and \c{highest - 1}. That class can also be used to obtain
|
||||
quantities larger than 32 bits.
|
||||
|
||||
For example, to obtain a value between 0 and 255 (inclusive), one would write:
|
||||
|
|
@ -685,11 +813,11 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
*/
|
||||
|
||||
/*!
|
||||
\fn quint32 QRandomGenerator::bounded(int sup)
|
||||
\fn quint32 QRandomGenerator::bounded(int highest)
|
||||
\overload
|
||||
|
||||
Generates one random 32-bit quantity in the range between 0 (inclusive) and
|
||||
\a sup (exclusive). \a sup must not be negative.
|
||||
\a highest (exclusive). \a highest must not be negative.
|
||||
|
||||
Note that this function cannot be used to obtain values in the full 32-bit
|
||||
range of int. Instead, use generate() and cast to int.
|
||||
|
|
@ -698,13 +826,13 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
*/
|
||||
|
||||
/*!
|
||||
\fn quint32 QRandomGenerator::bounded(quint32 min, quint32 sup)
|
||||
\fn quint32 QRandomGenerator::bounded(quint32 lowest, quint32 highest)
|
||||
\overload
|
||||
|
||||
Generates one random 32-bit quantity in the range between \a min (inclusive)
|
||||
and \a sup (exclusive). The same result may also be obtained by using
|
||||
Generates one random 32-bit quantity in the range between \a lowest (inclusive)
|
||||
and \a highest (exclusive). The same result may also be obtained by using
|
||||
\c{\l{http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution}{std::uniform_int_distribution}}
|
||||
with parameters \a min and \c{\a sup - 1}. That class can also be used to
|
||||
with parameters \a lowest and \c{\a highest - 1}. That class can also be used to
|
||||
obtain quantities larger than 32 bits.
|
||||
|
||||
For example, to obtain a value between 1000 (incl.) and 2000 (excl.), one
|
||||
|
|
@ -722,11 +850,11 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
*/
|
||||
|
||||
/*!
|
||||
\fn quint32 QRandomGenerator::bounded(int min, int sup)
|
||||
\fn quint32 QRandomGenerator::bounded(int lowest, int highest)
|
||||
\overload
|
||||
|
||||
Generates one random 32-bit quantity in the range between \a min
|
||||
(inclusive) and \a sup (exclusive), both of which may be negative.
|
||||
Generates one random 32-bit quantity in the range between \a lowest
|
||||
(inclusive) and \a highest (exclusive), both of which may be negative.
|
||||
|
||||
Note that this function cannot be used to obtain values in the full 32-bit
|
||||
range of int. Instead, use generate() and cast to int.
|
||||
|
|
@ -734,6 +862,54 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
\sa generate(), generate64(), generateDouble()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator *QRandomGenerator::system()
|
||||
\threadsafe
|
||||
|
||||
Returns a pointer to a shared QRandomGenerator that always uses the
|
||||
facilities provided by the operating system to generate random numbers. The
|
||||
system facilities are considered to be cryptographically safe on at least
|
||||
the following operating systems: Apple OSes (Darwin), BSDs, Linux, Windows.
|
||||
That may also be the case on other operating systems.
|
||||
|
||||
They are also possibly backed by a true hardware random number generator.
|
||||
For that reason, the QRandomGenerator returned by this function should not
|
||||
be used for bulk data generation. Instead, use it to seed QRandomGenerator
|
||||
or a random engine from the <random> header.
|
||||
|
||||
The object returned by this function is thread-safe and may be used in any
|
||||
thread without locks. It may also be copied and the resulting
|
||||
QRandomGenerator will also access the operating system facilities, but they
|
||||
will not generate the same sequence.
|
||||
|
||||
\sa global()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator *QRandomGenerator::global()
|
||||
\threadsafe
|
||||
|
||||
Returns a pointer to a shared QRandomGenerator that was seeded using
|
||||
QRandomGenerator::system(). This function should be used to create random data
|
||||
without the expensive creation of a securely-seeded QRandomGenerator for a
|
||||
specific use or storing the rather large QRandomGenerator object.
|
||||
large QRandomGenerator object.
|
||||
|
||||
For example, the following creates a random RGB color:
|
||||
|
||||
\code
|
||||
return QColor::fromRgb(QRandomGenerator::global()->generate());
|
||||
\endcode
|
||||
|
||||
Accesses to this object are thread-safe and it may therefore be used in any
|
||||
thread without locks. The object may also be copied and the sequence
|
||||
produced by the copy will be the same as the shared object will produce.
|
||||
Note, however, that if there are other threads accessing the global object,
|
||||
those threads may obtain samples at unpredictable intervals.
|
||||
|
||||
\sa system()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QRandomGenerator64
|
||||
\inmodule QtCore
|
||||
|
|
@ -755,10 +931,11 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
*/
|
||||
|
||||
/*!
|
||||
\fn QRandomGenerator64::QRandomGenerator64()
|
||||
\internal
|
||||
Defaulted constructor, does nothing.
|
||||
*/
|
||||
\fn QRandomGenerator64::QRandomGenerator64(const QRandomGenerator &other)
|
||||
\internal
|
||||
|
||||
Creates a copy.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QRandomGenerator64::result_type
|
||||
|
|
@ -793,80 +970,78 @@ static Q_NEVER_INLINE void fill(void *begin, void *end)
|
|||
\sa QRandomGenerator::generate(), QRandomGenerator::generate64()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn double QRandomGenerator64::entropy() const
|
||||
|
||||
Returns the estimate of the entropy in the random generator source.
|
||||
|
||||
This function exists to comply with the Standard Library requirements for
|
||||
\c{\l{http://en.cppreference.com/w/cpp/numeric/random/random_device}{std::random_device}}
|
||||
but it does not and cannot ever work. It is not possible to obtain a
|
||||
reliable entropy value in a shared entropy pool in a multi-tasking system,
|
||||
as other processes or threads may use that entropy. Any value non-zero
|
||||
value that this function could return would be obsolete by the time the
|
||||
user code reached it.
|
||||
|
||||
Since QRandomGenerator64 attempts to use a hardware Random Number Generator,
|
||||
this function always returns 0.0.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn result_type QRandomGenerator64::min()
|
||||
|
||||
Returns the minimum value that QRandomGenerator64 may ever generate. That is, 0.
|
||||
|
||||
\sa max(), QRandomGenerator::max()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn result_type QRandomGenerator64::max()
|
||||
|
||||
Returns the maximum value that QRandomGenerator64 may ever generate. That is,
|
||||
\c {std::numeric_limits<result_type>::max()}.
|
||||
|
||||
\sa min(), QRandomGenerator::max()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Generates one 32-bit random value and returns it.
|
||||
|
||||
Note about casting to a signed integer: all bits returned by this function
|
||||
are random, so there's a 50% chance that the most significant bit will be
|
||||
set. If you wish to cast the returned value to int and keep it positive,
|
||||
you should mask the sign bit off:
|
||||
|
||||
\code
|
||||
int value = QRandomGenerator::generate() & std::numeric_limits<int>::max();
|
||||
\endcode
|
||||
|
||||
\sa generate64(), generateDouble()
|
||||
*/
|
||||
quint32 QRandomGenerator::generate()
|
||||
inline QRandomGenerator::Storage::Storage()
|
||||
{
|
||||
quint32 ret;
|
||||
fill(&ret, &ret + 1);
|
||||
return ret;
|
||||
// nothing
|
||||
}
|
||||
|
||||
/*!
|
||||
Generates one 64-bit random value and returns it.
|
||||
|
||||
Note about casting to a signed integer: all bits returned by this function
|
||||
are random, so there's a 50% chance that the most significant bit will be
|
||||
set. If you wish to cast the returned value to qint64 and keep it positive,
|
||||
you should mask the sign bit off:
|
||||
|
||||
\code
|
||||
qint64 value = QRandomGenerator::generate64() & std::numeric_limits<qint64>::max();
|
||||
\endcode
|
||||
|
||||
\sa generate(), generateDouble(), QRandomGenerator64
|
||||
*/
|
||||
quint64 QRandomGenerator::generate64()
|
||||
inline QRandomGenerator64::QRandomGenerator64(System s)
|
||||
: QRandomGenerator(s)
|
||||
{
|
||||
quint64 ret;
|
||||
fill(&ret, &ret + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
QRandomGenerator64 *QRandomGenerator64::system()
|
||||
{
|
||||
static QRandomGenerator64 system(System{});
|
||||
return &system;
|
||||
}
|
||||
|
||||
QRandomGenerator64 *QRandomGenerator64::global()
|
||||
{
|
||||
PRNGLocker lock(nullptr);
|
||||
static QRandomGenerator64 global(System{});
|
||||
if (global.type == SystemRNG) {
|
||||
// seed with the system CSPRNG and change the type
|
||||
new (&global.storage.engine()) RandomEngine(static_cast<SystemGenerator &>(system()->storage.sys));
|
||||
global.type = MersenneTwister;
|
||||
}
|
||||
|
||||
return &global;
|
||||
}
|
||||
|
||||
/// \internal
|
||||
inline QRandomGenerator::QRandomGenerator(System)
|
||||
: type(SystemRNG)
|
||||
{
|
||||
Q_STATIC_ASSERT(sizeof(storage) >= sizeof(SystemGenerator));
|
||||
new (&storage) SystemGenerator();
|
||||
}
|
||||
|
||||
|
||||
QRandomGenerator::QRandomGenerator(const QRandomGenerator &other)
|
||||
: type(other.type)
|
||||
{
|
||||
if (type != SystemRNG) {
|
||||
PRNGLocker lock(&other);
|
||||
storage.engine() = other.storage.engine();
|
||||
}
|
||||
}
|
||||
|
||||
QRandomGenerator &QRandomGenerator::operator=(const QRandomGenerator &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
if (Q_UNLIKELY(this == system()) || Q_UNLIKELY(this == global()))
|
||||
qFatal("Attempted to overwrite a QRandomGenerator to system() or global().");
|
||||
|
||||
if ((type = other.type) != SystemRNG) {
|
||||
PRNGLocker lock(&other);
|
||||
storage.engine() = other.storage.engine();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
QRandomGenerator::QRandomGenerator(std::seed_seq &sseq) Q_DECL_NOTHROW
|
||||
: type(MersenneTwister)
|
||||
{
|
||||
new (&storage.engine()) RandomEngine(sseq);
|
||||
}
|
||||
|
||||
QRandomGenerator::QRandomGenerator(const quint32 *begin, const quint32 *end)
|
||||
: type(MersenneTwister)
|
||||
{
|
||||
std::seed_seq s(begin, end);
|
||||
new (&storage.engine()) RandomEngine(s);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
@ -875,9 +1050,19 @@ quint64 QRandomGenerator::generate64()
|
|||
Fills the range pointed by \a buffer and \a bufferEnd with 32-bit random
|
||||
values. The buffer must be correctly aligned.
|
||||
*/
|
||||
void QRandomGenerator::fillRange_helper(void *buffer, void *bufferEnd)
|
||||
void QRandomGenerator::_fillRange(void *buffer, void *bufferEnd)
|
||||
{
|
||||
fill(buffer, bufferEnd);
|
||||
// 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);
|
||||
|
||||
if (type == SystemRNG || Q_UNLIKELY(uint(qt_randomdevice_control) & (UseSystemRNG|SetRandomData)))
|
||||
return SystemGenerator::self().generate(begin, end);
|
||||
|
||||
PRNGLocker lock(this);
|
||||
std::generate(begin, end, [this]() { return storage.engine()(); });
|
||||
}
|
||||
|
||||
#if defined(Q_OS_ANDROID) && (__ANDROID_API__ < 21)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <algorithm> // for std::generate
|
||||
#include <random> // for std::mt19937
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -51,19 +52,37 @@ class QRandomGenerator
|
|||
template <typename UInt> using IfValidUInt =
|
||||
typename std::enable_if<std::is_unsigned<UInt>::value && sizeof(UInt) >= sizeof(uint), bool>::type;
|
||||
public:
|
||||
static QRandomGenerator system() { return {}; }
|
||||
static QRandomGenerator global() { return {}; }
|
||||
QRandomGenerator() = default;
|
||||
QRandomGenerator(quint32 seed = 1)
|
||||
: QRandomGenerator(&seed, 1)
|
||||
{}
|
||||
template <qssize_t N> QRandomGenerator(const quint32 (&seedBuffer)[N])
|
||||
: QRandomGenerator(seedBuffer, seedBuffer + N)
|
||||
{}
|
||||
QRandomGenerator(const quint32 *seedBuffer, qssize_t len)
|
||||
: QRandomGenerator(seedBuffer, seedBuffer + len)
|
||||
{}
|
||||
Q_CORE_EXPORT QRandomGenerator(std::seed_seq &sseq) Q_DECL_NOTHROW;
|
||||
Q_CORE_EXPORT QRandomGenerator(const quint32 *begin, const quint32 *end);
|
||||
|
||||
// ### REMOVE BEFORE 5.10
|
||||
QRandomGenerator *operator->() { return this; }
|
||||
static quint32 get32() { return generate(); }
|
||||
static quint64 get64() { return generate64(); }
|
||||
static qreal getReal() { return generateDouble(); }
|
||||
// copy constructor & assignment operator (move unnecessary)
|
||||
Q_CORE_EXPORT QRandomGenerator(const QRandomGenerator &other);
|
||||
Q_CORE_EXPORT QRandomGenerator &operator=(const QRandomGenerator &other);
|
||||
|
||||
static Q_CORE_EXPORT quint32 generate();
|
||||
static Q_CORE_EXPORT quint64 generate64();
|
||||
static double generateDouble()
|
||||
quint32 generate()
|
||||
{
|
||||
quint32 ret;
|
||||
fillRange(&ret, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
quint64 generate64()
|
||||
{
|
||||
quint32 buf[2];
|
||||
fillRange(buf);
|
||||
return buf[0] | (quint64(buf[1]) << 32);
|
||||
}
|
||||
|
||||
double generateDouble()
|
||||
{
|
||||
// IEEE 754 double precision has:
|
||||
// 1 bit sign
|
||||
|
|
@ -77,87 +96,144 @@ public:
|
|||
return double(x) / double(limit);
|
||||
}
|
||||
|
||||
static qreal bounded(qreal sup)
|
||||
double bounded(double highest)
|
||||
{
|
||||
return generateDouble() * sup;
|
||||
return generateDouble() * highest;
|
||||
}
|
||||
|
||||
static quint32 bounded(quint32 sup)
|
||||
quint32 bounded(quint32 highest)
|
||||
{
|
||||
quint64 value = generate();
|
||||
value *= sup;
|
||||
value *= highest;
|
||||
value /= (max)() + quint64(1);
|
||||
return quint32(value);
|
||||
}
|
||||
|
||||
static int bounded(int sup)
|
||||
int bounded(int highest)
|
||||
{
|
||||
return int(bounded(quint32(sup)));
|
||||
return int(bounded(quint32(highest)));
|
||||
}
|
||||
|
||||
static quint32 bounded(quint32 min, quint32 sup)
|
||||
quint32 bounded(quint32 lowest, quint32 highest)
|
||||
{
|
||||
return bounded(sup - min) + min;
|
||||
return bounded(highest - lowest) + lowest;
|
||||
}
|
||||
|
||||
static int bounded(int min, int sup)
|
||||
int bounded(int lowest, int highest)
|
||||
{
|
||||
return bounded(sup - min) + min;
|
||||
return bounded(highest - lowest) + lowest;
|
||||
}
|
||||
|
||||
template <typename UInt, IfValidUInt<UInt> = true>
|
||||
static void fillRange(UInt *buffer, qssize_t count)
|
||||
void fillRange(UInt *buffer, qssize_t count)
|
||||
{
|
||||
fillRange_helper(buffer, buffer + count);
|
||||
_fillRange(buffer, buffer + count);
|
||||
}
|
||||
|
||||
template <typename UInt, size_t N, IfValidUInt<UInt> = true>
|
||||
static void fillRange(UInt (&buffer)[N])
|
||||
void fillRange(UInt (&buffer)[N])
|
||||
{
|
||||
fillRange_helper(buffer, buffer + N);
|
||||
_fillRange(buffer, buffer + N);
|
||||
}
|
||||
|
||||
// API like std::seed_seq
|
||||
template <typename ForwardIterator>
|
||||
void generate(ForwardIterator begin, ForwardIterator end)
|
||||
{
|
||||
auto generator = static_cast<quint32 (*)()>(&QRandomGenerator::generate);
|
||||
std::generate(begin, end, generator);
|
||||
std::generate(begin, end, [this]() { return generate(); });
|
||||
}
|
||||
|
||||
void generate(quint32 *begin, quint32 *end)
|
||||
{
|
||||
fillRange_helper(begin, end);
|
||||
_fillRange(begin, end);
|
||||
}
|
||||
|
||||
// API like std::random_device
|
||||
// API like std:: random engines
|
||||
typedef quint32 result_type;
|
||||
result_type operator()() { return generate(); }
|
||||
double entropy() const Q_DECL_NOTHROW { return 0.0; }
|
||||
static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits<result_type>::min)(); }
|
||||
static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits<result_type>::max)(); }
|
||||
|
||||
static inline QRandomGenerator *system();
|
||||
static inline QRandomGenerator *global();
|
||||
|
||||
protected:
|
||||
enum System {};
|
||||
QRandomGenerator(System);
|
||||
|
||||
private:
|
||||
static Q_CORE_EXPORT void fillRange_helper(void *buffer, void *bufferEnd);
|
||||
Q_CORE_EXPORT void _fillRange(void *buffer, void *bufferEnd);
|
||||
|
||||
friend class QRandomGenerator64;
|
||||
struct SystemGeneratorBase {};
|
||||
struct SystemGenerator;
|
||||
typedef std::mt19937 RandomEngine;
|
||||
|
||||
union Storage {
|
||||
SystemGeneratorBase sys;
|
||||
#ifdef Q_COMPILER_UNRESTRICTED_UNIONS
|
||||
RandomEngine twister;
|
||||
RandomEngine &engine() { return twister; }
|
||||
const RandomEngine &engine() const { return twister; }
|
||||
#else
|
||||
std::aligned_storage<sizeof(RandomEngine), Q_ALIGNOF(RandomEngine)>::type buffer;
|
||||
RandomEngine &engine() { return reinterpret_cast<RandomEngine &>(buffer); }
|
||||
const RandomEngine &engine() const { return reinterpret_cast<const RandomEngine &>(buffer); }
|
||||
#endif
|
||||
|
||||
Q_STATIC_ASSERT_X(std::is_trivially_destructible<RandomEngine>::value,
|
||||
"std::mersenne_twister not trivially destructible as expected");
|
||||
Storage();
|
||||
};
|
||||
uint type;
|
||||
Storage storage;
|
||||
};
|
||||
|
||||
class QRandomGenerator64
|
||||
class QRandomGenerator64 : public QRandomGenerator
|
||||
{
|
||||
QRandomGenerator64(System);
|
||||
public:
|
||||
static QRandomGenerator64 system() { return {}; }
|
||||
static QRandomGenerator64 global() { return {}; }
|
||||
QRandomGenerator64() = default;
|
||||
// unshadow generate() overloads, since we'll override.
|
||||
using QRandomGenerator::generate;
|
||||
quint64 generate() { return generate64(); }
|
||||
|
||||
static quint64 generate() { return QRandomGenerator::generate64(); }
|
||||
|
||||
// API like std::random_device
|
||||
typedef quint64 result_type;
|
||||
result_type operator()() { return QRandomGenerator::generate64(); }
|
||||
double entropy() const Q_DECL_NOTHROW { return 0.0; }
|
||||
result_type operator()() { return generate64(); }
|
||||
|
||||
#ifndef Q_QDOC
|
||||
QRandomGenerator64(quint32 seed = 1)
|
||||
: QRandomGenerator(seed)
|
||||
{}
|
||||
template <qssize_t N> QRandomGenerator64(const quint32 (&seedBuffer)[N])
|
||||
: QRandomGenerator(seedBuffer)
|
||||
{}
|
||||
QRandomGenerator64(const quint32 *seedBuffer, qssize_t len)
|
||||
: QRandomGenerator(seedBuffer, len)
|
||||
{}
|
||||
QRandomGenerator64(std::seed_seq &sseq) Q_DECL_NOTHROW
|
||||
: QRandomGenerator(sseq)
|
||||
{}
|
||||
QRandomGenerator64(const quint32 *begin, const quint32 *end)
|
||||
: QRandomGenerator(begin, end)
|
||||
{}
|
||||
QRandomGenerator64(const QRandomGenerator &other) : QRandomGenerator(other) {}
|
||||
|
||||
static Q_DECL_CONSTEXPR result_type min() { return (std::numeric_limits<result_type>::min)(); }
|
||||
static Q_DECL_CONSTEXPR result_type max() { return (std::numeric_limits<result_type>::max)(); }
|
||||
static Q_CORE_EXPORT QRandomGenerator64 *system();
|
||||
static Q_CORE_EXPORT QRandomGenerator64 *global();
|
||||
#endif // Q_QDOC
|
||||
};
|
||||
|
||||
inline QRandomGenerator *QRandomGenerator::system()
|
||||
{
|
||||
return QRandomGenerator64::system();
|
||||
}
|
||||
|
||||
inline QRandomGenerator *QRandomGenerator::global()
|
||||
{
|
||||
return QRandomGenerator64::global();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
|
|||
|
|
@ -52,10 +52,12 @@
|
|||
//
|
||||
|
||||
#include "qglobal_p.h"
|
||||
#include <private/qsimd_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum QRandomGeneratorControl {
|
||||
UseSystemRNG = 1,
|
||||
SkipSystemRNG = 2,
|
||||
SkipHWRNG = 4,
|
||||
SetRandomData = 8,
|
||||
|
|
@ -64,6 +66,11 @@ enum QRandomGeneratorControl {
|
|||
RandomDataMask = 0xfffffff0
|
||||
};
|
||||
|
||||
enum RNGType {
|
||||
SystemRNG = 0,
|
||||
MersenneTwister = 1
|
||||
};
|
||||
|
||||
#if defined(QT_BUILD_INTERNAL) && defined(QT_BUILD_CORE_LIB)
|
||||
Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
|
||||
#elif defined(QT_BUILD_INTERNAL)
|
||||
|
|
@ -72,6 +79,16 @@ extern Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control;
|
|||
enum { qt_randomdevice_control = 0 };
|
||||
#endif
|
||||
|
||||
inline bool qt_has_hwrng()
|
||||
{
|
||||
#if defined(Q_PROCESSOR_X86) && QT_COMPILER_SUPPORTS_HERE(RDRND)
|
||||
return qCpuHasFeature(RDRND);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QRANDOM_P_H
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@
|
|||
#define COMMA ,
|
||||
#define QVERIFY_3TIMES(statement) \
|
||||
do {\
|
||||
if (!QTest::qVerify(static_cast<bool>(statement), #statement, "1st try", __FILE__, __LINE__))\
|
||||
if (!QTest::qVerify(static_cast<bool>(statement), #statement, "2nd try", __FILE__, __LINE__))\
|
||||
if (!static_cast<bool>(statement))\
|
||||
if (!static_cast<bool>(statement))\
|
||||
if (!QTest::qVerify(static_cast<bool>(statement), #statement, "3rd try", __FILE__, __LINE__))\
|
||||
return;\
|
||||
} while (0)
|
||||
|
|
@ -71,6 +71,13 @@ public slots:
|
|||
void cleanup() { setRNGControl(0); }
|
||||
|
||||
private slots:
|
||||
void basics();
|
||||
void knownSequence();
|
||||
void copying();
|
||||
void copyingGlobal();
|
||||
void copyingSystem();
|
||||
void systemRng();
|
||||
|
||||
void generate32_data();
|
||||
void generate32();
|
||||
void generate64_data() { generate32_data(); }
|
||||
|
|
@ -110,18 +117,154 @@ private slots:
|
|||
void stdRandomDistributions();
|
||||
};
|
||||
|
||||
// The first 20 results of the sequence:
|
||||
static const quint32 defaultRngResults[] = {
|
||||
853323747U, 2396352728U, 3025954838U, 2985633182U, 2815751046U,
|
||||
340588426U, 3587208406U, 298087538U, 2912478009U, 3642122814U,
|
||||
3202916223U, 799257577U, 1872145992U, 639469699U, 3201121432U,
|
||||
2388658094U, 1735523408U, 2215232359U, 668106566U, 2554687763U
|
||||
};
|
||||
|
||||
|
||||
using namespace std;
|
||||
QT_WARNING_DISABLE_GCC("-Wfloat-equal")
|
||||
QT_WARNING_DISABLE_CLANG("-Wfloat-equal")
|
||||
|
||||
struct RandomGenerator : public QRandomGenerator
|
||||
{
|
||||
RandomGenerator(uint control)
|
||||
: QRandomGenerator(control ?
|
||||
QRandomGenerator(control & RandomDataMask) :
|
||||
*QRandomGenerator::global())
|
||||
{
|
||||
setRNGControl(control);
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QRandomGenerator::basics()
|
||||
{
|
||||
// default constructible
|
||||
QRandomGenerator rng;
|
||||
|
||||
// copyable && movable
|
||||
rng = rng;
|
||||
rng = std::move(rng);
|
||||
|
||||
// 64-bit
|
||||
QRandomGenerator64 rng64;
|
||||
rng64 = rng64;
|
||||
rng64 = std::move(rng64);
|
||||
|
||||
// 32- and 64-bit should be interchangeable:
|
||||
rng = rng64;
|
||||
rng64 = rng;
|
||||
rng = std::move(rng64);
|
||||
rng64 = std::move(rng);
|
||||
|
||||
// access global
|
||||
QRandomGenerator *global = QRandomGenerator::global();
|
||||
QRandomGenerator globalCopy = *global;
|
||||
globalCopy = *global;
|
||||
QRandomGenerator64 *global64 = QRandomGenerator64::global();
|
||||
QRandomGenerator64 globalCopy64 = *global64;
|
||||
globalCopy64 = *global64;
|
||||
|
||||
// access system
|
||||
QRandomGenerator *system = QRandomGenerator::system();
|
||||
QRandomGenerator systemRng = *system;
|
||||
systemRng = *system;
|
||||
|
||||
QRandomGenerator64 *system64 = QRandomGenerator64::system();
|
||||
QRandomGenerator64 systemRng64 = *system64;
|
||||
systemRng64 = *system64;
|
||||
|
||||
Q_STATIC_ASSERT(std::is_same<decltype(rng64.generate()) COMMA quint64>::value);
|
||||
Q_STATIC_ASSERT(std::is_same<decltype(system64->generate()) COMMA quint64>::value);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::knownSequence()
|
||||
{
|
||||
QRandomGenerator rng;
|
||||
for (quint32 x : defaultRngResults)
|
||||
QCOMPARE(rng(), x);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::copying()
|
||||
{
|
||||
QRandomGenerator rng1;
|
||||
QRandomGenerator rng2 = rng1;
|
||||
|
||||
quint32 samples[20];
|
||||
rng1.fillRange(samples);
|
||||
|
||||
// should produce the same sequence, whichever it was
|
||||
for (quint32 x : samples)
|
||||
QCOMPARE(rng2(), x);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::copyingGlobal()
|
||||
{
|
||||
QRandomGenerator &global = *QRandomGenerator::global();
|
||||
QRandomGenerator copy = global;
|
||||
|
||||
quint32 samples[20];
|
||||
global.fillRange(samples);
|
||||
|
||||
// should produce the same sequence, whichever it was
|
||||
for (quint32 x : samples)
|
||||
QCOMPARE(copy(), x);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::copyingSystem()
|
||||
{
|
||||
QRandomGenerator &system = *QRandomGenerator::system();
|
||||
QRandomGenerator copy = system;
|
||||
QRandomGenerator copy2 = copy;
|
||||
copy2 = copy;
|
||||
|
||||
quint32 samples[20];
|
||||
copy2.fillRange(samples);
|
||||
|
||||
// should NOT produce the same sequence, whichever it was
|
||||
int sameCount = 0;
|
||||
for (quint32 x : samples)
|
||||
sameCount += (copy() == x);
|
||||
QVERIFY(sameCount < 20);
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::systemRng()
|
||||
{
|
||||
QRandomGenerator *rng = QRandomGenerator::system();
|
||||
rng->generate();
|
||||
rng->generate64();
|
||||
rng->generateDouble();
|
||||
rng->bounded(100);
|
||||
rng->bounded(100U);
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
quint32 setpoint = std::numeric_limits<int>::max();
|
||||
++setpoint;
|
||||
quint64 setpoint64 = quint64(setpoint) << 32 | setpoint;
|
||||
setRNGControl(SetRandomData | setpoint);
|
||||
|
||||
QCOMPARE(rng->generate(), setpoint);
|
||||
QCOMPARE(rng->generate64(), setpoint64);
|
||||
QCOMPARE(rng->generateDouble(), ldexp(setpoint64, -64));
|
||||
QCOMPARE(rng->bounded(100), 50);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::generate32_data()
|
||||
{
|
||||
QTest::addColumn<uint>("control");
|
||||
QTest::newRow("default") << 0U;
|
||||
QTest::newRow("fixed") << (RandomValue32 & RandomDataMask);
|
||||
QTest::newRow("global") << 0U;
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
QTest::newRow("system") << uint(SkipHWRNG);
|
||||
if (qt_has_hwrng())
|
||||
QTest::newRow("hwrng") << uint(UseSystemRNG);
|
||||
QTest::newRow("system") << uint(UseSystemRNG | SkipHWRNG);
|
||||
# ifdef HAVE_FALLBACK_ENGINE
|
||||
QTest::newRow("fallback") << uint(SkipHWRNG | SkipSystemRNG);
|
||||
QTest::newRow("system-fallback") << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG);
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
|
@ -129,39 +272,40 @@ void tst_QRandomGenerator::generate32_data()
|
|||
void tst_QRandomGenerator::generate32()
|
||||
{
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QVERIFY_3TIMES([] {
|
||||
quint32 value = QRandomGenerator::generate();
|
||||
QVERIFY_3TIMES([&] {
|
||||
quint32 value = rng.generate();
|
||||
return value != 0 && value != RandomValue32;
|
||||
}());
|
||||
}
|
||||
|
||||
// and should hopefully be different from repeated calls
|
||||
for (int i = 0; i < 4; ++i)
|
||||
QVERIFY_3TIMES(QRandomGenerator::generate() != QRandomGenerator::generate());
|
||||
QVERIFY_3TIMES(rng.generate() != rng.generate());
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::generate64()
|
||||
{
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
QVERIFY_3TIMES(rng.generate64() > std::numeric_limits<quint32>::max());
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QVERIFY_3TIMES([] {
|
||||
quint64 value = QRandomGenerator::generate();
|
||||
QVERIFY_3TIMES([&] {
|
||||
quint64 value = rng.generate64();
|
||||
return value != 0 && value != RandomValue32 && value != RandomValue64;
|
||||
}());
|
||||
}
|
||||
|
||||
// and should hopefully be different from repeated calls
|
||||
for (int i = 0; i < 4; ++i)
|
||||
QVERIFY_3TIMES(QRandomGenerator::generate64() != QRandomGenerator::generate64());
|
||||
QVERIFY_3TIMES(rng.generate64() != rng.generate64());
|
||||
for (int i = 0; i < 4; ++i)
|
||||
QVERIFY_3TIMES(QRandomGenerator::generate() != quint32(QRandomGenerator::generate64()));
|
||||
QVERIFY_3TIMES(rng.generate() != quint32(rng.generate64()));
|
||||
for (int i = 0; i < 4; ++i)
|
||||
QVERIFY_3TIMES(QRandomGenerator::generate() != (QRandomGenerator::generate64() >> 32));
|
||||
QVERIFY_3TIMES(rng.generate() != (rng.generate64() >> 32));
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::quality()
|
||||
|
|
@ -190,7 +334,9 @@ void tst_QRandomGenerator::quality()
|
|||
Q_STATIC_ASSERT(FailureThreshold > AcceptableThreshold);
|
||||
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
if (control & RandomDataMask)
|
||||
return;
|
||||
RandomGenerator rng(control);
|
||||
|
||||
int histogram[UCHAR_MAX + 1];
|
||||
memset(histogram, 0, sizeof(histogram));
|
||||
|
|
@ -199,7 +345,7 @@ void tst_QRandomGenerator::quality()
|
|||
// test the quality of the generator
|
||||
quint32 buffer[BufferCount];
|
||||
memset(buffer, 0xcc, sizeof(buffer));
|
||||
generate_n(buffer, +BufferCount, [] { return QRandomGenerator::generate(); });
|
||||
generate_n(buffer, +BufferCount, [&] { return rng.generate(); });
|
||||
|
||||
quint8 *ptr = reinterpret_cast<quint8 *>(buffer);
|
||||
quint8 *end = ptr + sizeof(buffer);
|
||||
|
|
@ -224,20 +370,20 @@ void tst_QRandomGenerator::quality()
|
|||
template <typename T> void fillRange_template()
|
||||
{
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QVERIFY_3TIMES([] {
|
||||
QVERIFY_3TIMES([&] {
|
||||
T value[1] = { RandomValue32 };
|
||||
QRandomGenerator::fillRange(value);
|
||||
rng.fillRange(value);
|
||||
return value[0] != 0 && value[0] != RandomValue32;
|
||||
}());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QVERIFY_3TIMES([] {
|
||||
QVERIFY_3TIMES([&] {
|
||||
T array[2] = {};
|
||||
QRandomGenerator::fillRange(array);
|
||||
rng.fillRange(array);
|
||||
return array[0] != array[1];
|
||||
}());
|
||||
}
|
||||
|
|
@ -245,18 +391,18 @@ template <typename T> void fillRange_template()
|
|||
if (sizeof(T) > sizeof(quint32)) {
|
||||
// just to shut up a warning about shifting uint more than the width
|
||||
enum { Shift = sizeof(T) / 2 * CHAR_BIT };
|
||||
QVERIFY_3TIMES([] {
|
||||
QVERIFY_3TIMES([&] {
|
||||
T value[1] = { };
|
||||
QRandomGenerator::fillRange(value);
|
||||
rng.fillRange(value);
|
||||
return quint32(value[0] >> Shift) != quint32(value[0]);
|
||||
}());
|
||||
}
|
||||
|
||||
// fill in a longer range
|
||||
auto longerArrayCheck = [] {
|
||||
auto longerArrayCheck = [&] {
|
||||
T array[32];
|
||||
memset(array, 0, sizeof(array));
|
||||
QRandomGenerator::fillRange(array);
|
||||
rng.fillRange(array);
|
||||
if (sizeof(T) == sizeof(RandomValue64)
|
||||
&& find(begin(array), end(array), RandomValue64) != end(array))
|
||||
return false;
|
||||
|
|
@ -273,11 +419,11 @@ void tst_QRandomGenerator::fillRangeULLong() { fillRange_template<qulonglong>();
|
|||
template <typename T> void generate_template()
|
||||
{
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
// almost the same as fillRange, but limited to 32 bits
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QVERIFY_3TIMES([] {
|
||||
QVERIFY_3TIMES([&] {
|
||||
T value[1] = { RandomValue32 };
|
||||
QRandomGenerator().generate(begin(value), end(value));
|
||||
return value[0] != 0 && value[0] != RandomValue32
|
||||
|
|
@ -286,10 +432,10 @@ template <typename T> void generate_template()
|
|||
}
|
||||
|
||||
// fill in a longer range
|
||||
auto longerArrayCheck = [] {
|
||||
auto longerArrayCheck = [&] {
|
||||
T array[72] = {}; // at least 256 bytes
|
||||
QRandomGenerator().generate(begin(array), end(array));
|
||||
return find_if(begin(array), end(array), [](T cur) {
|
||||
return find_if(begin(array), end(array), [&](T cur) {
|
||||
return cur == 0 || cur == RandomValue32 ||
|
||||
cur == RandomValue64 || cur > numeric_limits<quint32>::max();
|
||||
}) == end(array);
|
||||
|
|
@ -303,12 +449,12 @@ void tst_QRandomGenerator::generateULLong() { generate_template<qulonglong>(); }
|
|||
void tst_QRandomGenerator::generateNonContiguous()
|
||||
{
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
QLinkedList<quint64> list = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
auto longerArrayCheck = [&] {
|
||||
QRandomGenerator().generate(list.begin(), list.end());
|
||||
return find_if(list.begin(), list.end(), [](quint64 cur) {
|
||||
return find_if(list.begin(), list.end(), [&](quint64 cur) {
|
||||
return cur == 0 || cur == RandomValue32 ||
|
||||
cur == RandomValue64 || cur > numeric_limits<quint32>::max();
|
||||
}) == list.end();
|
||||
|
|
@ -326,7 +472,7 @@ void tst_QRandomGenerator::bounded_data()
|
|||
QTest::addColumn<quint32>("sup");
|
||||
QTest::addColumn<quint32>("expected");
|
||||
|
||||
auto newRow = [](quint32 val, quint32 sup) {
|
||||
auto newRow = [&](quint32 val, quint32 sup) {
|
||||
// calculate the scaled value
|
||||
quint64 scaled = val;
|
||||
scaled <<= 32;
|
||||
|
|
@ -352,31 +498,31 @@ void tst_QRandomGenerator::bounded()
|
|||
QFETCH(uint, control);
|
||||
QFETCH(quint32, sup);
|
||||
QFETCH(quint32, expected);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
quint32 value = QRandomGenerator::bounded(sup);
|
||||
quint32 value = rng.bounded(sup);
|
||||
QVERIFY(value < sup);
|
||||
QCOMPARE(value, expected);
|
||||
|
||||
int ivalue = QRandomGenerator::bounded(sup);
|
||||
int ivalue = rng.bounded(sup);
|
||||
QVERIFY(ivalue < int(sup));
|
||||
QCOMPARE(ivalue, int(expected));
|
||||
|
||||
// confirm only the bound now
|
||||
setRNGControl(control & (SkipHWRNG|SkipSystemRNG));
|
||||
value = QRandomGenerator::bounded(sup);
|
||||
setRNGControl(control & (SkipHWRNG|SkipSystemRNG|UseSystemRNG));
|
||||
value = rng.bounded(sup);
|
||||
QVERIFY(value < sup);
|
||||
|
||||
value = QRandomGenerator::bounded(sup / 2, 3 * sup / 2);
|
||||
value = rng.bounded(sup / 2, 3 * sup / 2);
|
||||
QVERIFY(value >= sup / 2);
|
||||
QVERIFY(value < 3 * sup / 2);
|
||||
|
||||
ivalue = QRandomGenerator::bounded(-int(sup), int(sup));
|
||||
ivalue = rng.bounded(-int(sup), int(sup));
|
||||
QVERIFY(ivalue >= -int(sup));
|
||||
QVERIFY(ivalue < int(sup));
|
||||
|
||||
// wholly negative range
|
||||
ivalue = QRandomGenerator::bounded(-int(sup), 0);
|
||||
ivalue = rng.bounded(-int(sup), 0);
|
||||
QVERIFY(ivalue >= -int(sup));
|
||||
QVERIFY(ivalue < 0);
|
||||
}
|
||||
|
|
@ -407,7 +553,9 @@ void tst_QRandomGenerator::boundedQuality()
|
|||
Q_STATIC_ASSERT(FailureThreshold > AcceptableThreshold);
|
||||
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
if (control & RandomDataMask)
|
||||
return;
|
||||
RandomGenerator rng(control);
|
||||
|
||||
int histogram[Bound];
|
||||
memset(histogram, 0, sizeof(histogram));
|
||||
|
|
@ -415,7 +563,7 @@ void tst_QRandomGenerator::boundedQuality()
|
|||
{
|
||||
// test the quality of the generator
|
||||
QVector<quint32> buffer(BufferCount, 0xcdcdcdcd);
|
||||
generate(buffer.begin(), buffer.end(), [] { return QRandomGenerator::bounded(Bound); });
|
||||
generate(buffer.begin(), buffer.end(), [&] { return rng.bounded(Bound); });
|
||||
|
||||
for (quint32 value : qAsConst(buffer)) {
|
||||
QVERIFY(value < Bound);
|
||||
|
|
@ -441,24 +589,26 @@ void tst_QRandomGenerator::boundedQuality()
|
|||
void tst_QRandomGenerator::generateReal()
|
||||
{
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QVERIFY_3TIMES([] {
|
||||
qreal value = QRandomGenerator::generateDouble();
|
||||
QVERIFY_3TIMES([&] {
|
||||
qreal value = rng.generateDouble();
|
||||
return value >= 0 && value < 1 && value != RandomValueFP;
|
||||
}());
|
||||
}
|
||||
|
||||
// and should hopefully be different from repeated calls
|
||||
for (int i = 0; i < 4; ++i)
|
||||
QVERIFY_3TIMES(QRandomGenerator::generateDouble() != QRandomGenerator::generateDouble());
|
||||
QVERIFY_3TIMES(rng.generateDouble() != rng.generateDouble());
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::qualityReal()
|
||||
{
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
if (control & RandomDataMask)
|
||||
return;
|
||||
RandomGenerator rng(control);
|
||||
|
||||
enum {
|
||||
SampleSize = 160,
|
||||
|
|
@ -474,7 +624,7 @@ void tst_QRandomGenerator::qualityReal()
|
|||
};
|
||||
|
||||
double data[SampleSize];
|
||||
std::generate(std::begin(data), std::end(data), &QRandomGenerator::generateDouble);
|
||||
std::generate(std::begin(data), std::end(data), [&rng] { return rng.generateDouble(); });
|
||||
|
||||
int aboveHalf = 0;
|
||||
int belowOneEighth = 0;
|
||||
|
|
@ -503,12 +653,22 @@ void tst_QRandomGenerator::qualityReal()
|
|||
|
||||
template <typename Engine> void seedStdRandomEngine()
|
||||
{
|
||||
QRandomGenerator rd;
|
||||
Engine e(rd);
|
||||
QVERIFY_3TIMES(e() != 0);
|
||||
{
|
||||
QRandomGenerator &rd = *QRandomGenerator::system();
|
||||
Engine e(rd);
|
||||
QVERIFY_3TIMES(e() != 0);
|
||||
|
||||
e.seed(rd);
|
||||
QVERIFY_3TIMES(e() != 0);
|
||||
e.seed(rd);
|
||||
QVERIFY_3TIMES(e() != 0);
|
||||
}
|
||||
{
|
||||
QRandomGenerator64 &rd = *QRandomGenerator64::system();
|
||||
Engine e(rd);
|
||||
QVERIFY_3TIMES(e() != 0);
|
||||
|
||||
e.seed(rd);
|
||||
QVERIFY_3TIMES(e() != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::seedStdRandomEngines()
|
||||
|
|
@ -533,12 +693,16 @@ void tst_QRandomGenerator::stdUniformIntDistribution_data()
|
|||
QTest::addColumn<uint>("control");
|
||||
QTest::addColumn<quint32>("max");
|
||||
|
||||
auto newRow = [](quint32 max) {
|
||||
QTest::addRow("default:%u", max) << 0U << max;
|
||||
QTest::addRow("system:%u", max) << uint(SkipHWRNG) << max;
|
||||
#ifdef HAVE_FALLBACK_ENGINE
|
||||
QTest::addRow("fallback:%u", max) << uint(SkipHWRNG | SkipSystemRNG) << max;
|
||||
#endif
|
||||
auto newRow = [&](quint32 max) {
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
if (qt_has_hwrng())
|
||||
QTest::addRow("hwrng:%u", max) << uint(UseSystemRNG) << max;
|
||||
QTest::addRow("system:%u", max) << uint(UseSystemRNG | SkipHWRNG) << max;
|
||||
# ifdef HAVE_FALLBACK_ENGINE
|
||||
QTest::addRow("system-fallback:%u", max) << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG) << max;
|
||||
# endif
|
||||
#endif
|
||||
QTest::addRow("global:%u", max) << 0U << max;
|
||||
};
|
||||
|
||||
// useless: we can only generate zeroes:
|
||||
|
|
@ -553,7 +717,7 @@ void tst_QRandomGenerator::stdUniformIntDistribution()
|
|||
{
|
||||
QFETCH(uint, control);
|
||||
QFETCH(quint32, max);
|
||||
setRNGControl(control & (SkipHWRNG|SkipSystemRNG));
|
||||
RandomGenerator rng(control);
|
||||
|
||||
{
|
||||
QRandomGenerator rd;
|
||||
|
|
@ -621,21 +785,19 @@ void tst_QRandomGenerator::stdGenerateCanonical()
|
|||
QSKIP("MSVC 2013's std::generate_canonical is broken");
|
||||
#else
|
||||
QFETCH(uint, control);
|
||||
setRNGControl(control);
|
||||
RandomGenerator rng(control);
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
QVERIFY_3TIMES([] {
|
||||
QRandomGenerator rd;
|
||||
qreal value = std::generate_canonical<qreal COMMA 32>(rd);
|
||||
QVERIFY_3TIMES([&] {
|
||||
qreal value = std::generate_canonical<qreal COMMA 32>(rng);
|
||||
return value > 0 && value < 1 && value != RandomValueFP;
|
||||
}());
|
||||
}
|
||||
|
||||
// and should hopefully be different from repeated calls
|
||||
QRandomGenerator rd;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
QVERIFY_3TIMES(std::generate_canonical<qreal COMMA 32>(rd) !=
|
||||
std::generate_canonical<qreal COMMA 32>(rd));
|
||||
QVERIFY_3TIMES(std::generate_canonical<qreal COMMA 32>(rng) !=
|
||||
std::generate_canonical<qreal COMMA 32>(rng));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -649,12 +811,16 @@ void tst_QRandomGenerator::stdUniformRealDistribution_data()
|
|||
QTest::addColumn<double>("min");
|
||||
QTest::addColumn<double>("sup");
|
||||
|
||||
auto newRow = [](double min, double sup) {
|
||||
QTest::addRow("default:%g-%g", min, sup) << 0U << min << sup;
|
||||
QTest::addRow("system:%g-%g", min, sup) << uint(SkipHWRNG) << min << sup;
|
||||
#ifdef HAVE_FALLBACK_ENGINE
|
||||
QTest::addRow("fallback:%g-%g", min, sup) << uint(SkipHWRNG | SkipSystemRNG) << min << sup;
|
||||
#endif
|
||||
auto newRow = [&](double min, double sup) {
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
if (qt_has_hwrng())
|
||||
QTest::addRow("hwrng:%g-%g", min, sup) << uint(UseSystemRNG) << min << sup;
|
||||
QTest::addRow("system:%g-%g", min, sup) << uint(UseSystemRNG | SkipHWRNG) << min << sup;
|
||||
# ifdef HAVE_FALLBACK_ENGINE
|
||||
QTest::addRow("system-fallback:%g-%g", min, sup) << uint(UseSystemRNG | SkipHWRNG | SkipSystemRNG) << min << sup;
|
||||
# endif
|
||||
#endif
|
||||
QTest::addRow("global:%g-%g", min, sup) << 0U << min << sup;
|
||||
};
|
||||
|
||||
newRow(0, 0); // useless: we can only generate zeroes
|
||||
|
|
@ -670,7 +836,7 @@ void tst_QRandomGenerator::stdUniformRealDistribution()
|
|||
QFETCH(uint, control);
|
||||
QFETCH(double, min);
|
||||
QFETCH(double, sup);
|
||||
setRNGControl(control & (SkipHWRNG|SkipSystemRNG));
|
||||
RandomGenerator rng(control & (SkipHWRNG|SkipSystemRNG|UseSystemRNG));
|
||||
|
||||
{
|
||||
QRandomGenerator rd;
|
||||
|
|
@ -695,13 +861,9 @@ void tst_QRandomGenerator::stdUniformRealDistribution()
|
|||
}
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::stdRandomDistributions()
|
||||
template <typename Generator> void stdRandomDistributions_template()
|
||||
{
|
||||
// just a compile check for some of the distributions, besides
|
||||
// std::uniform_int_distribution and std::uniform_real_distribution (tested
|
||||
// above)
|
||||
|
||||
QRandomGenerator rd;
|
||||
Generator rd;
|
||||
|
||||
std::bernoulli_distribution()(rd);
|
||||
|
||||
|
|
@ -723,6 +885,16 @@ void tst_QRandomGenerator::stdRandomDistributions()
|
|||
}
|
||||
}
|
||||
|
||||
void tst_QRandomGenerator::stdRandomDistributions()
|
||||
{
|
||||
// just a compile check for some of the distributions, besides
|
||||
// std::uniform_int_distribution and std::uniform_real_distribution (tested
|
||||
// above)
|
||||
|
||||
stdRandomDistributions_template<QRandomGenerator>();
|
||||
stdRandomDistributions_template<QRandomGenerator64>();
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QRandomGenerator)
|
||||
|
||||
#include "tst_qrandomgenerator.moc"
|
||||
|
|
|
|||
Loading…
Reference in New Issue