734 lines
19 KiB
C
734 lines
19 KiB
C
|
/*
|
||
|
* mptRandom.h
|
||
|
* -----------
|
||
|
* Purpose: PRNG
|
||
|
* Notes : (currently none)
|
||
|
* Authors: OpenMPT Devs
|
||
|
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "BuildSettings.h"
|
||
|
|
||
|
#include "mptMutex.h"
|
||
|
|
||
|
#include <limits>
|
||
|
#include <random>
|
||
|
|
||
|
#ifdef MODPLUG_TRACKER
|
||
|
#include <cstdlib>
|
||
|
#endif // MODPLUG_TRACKER
|
||
|
|
||
|
|
||
|
OPENMPT_NAMESPACE_BEGIN
|
||
|
|
||
|
|
||
|
// NOTE:
|
||
|
// We implement our own PRNG and distribution functions as the implementations
|
||
|
// of std::uniform_int_distribution is either wrong (not uniform in MSVC2010) or
|
||
|
// not guaranteed to be livelock-free for bad PRNGs (in GCC, Clang, boost).
|
||
|
// We resort to a simpler implementation with only power-of-2 result ranges for
|
||
|
// both the underlying PRNG and our interface function. This saves us from
|
||
|
// complicated code having to deal with partial bits of entropy.
|
||
|
// Our interface still somewhat follows the mindset of C++11 <random> (with the
|
||
|
// addition of a simple wrapper function mpt::random which saves the caller from
|
||
|
// instantiating distribution objects for the common uniform distribution case.
|
||
|
// We are still using std::random_device for initial seeding when avalable and
|
||
|
// after working around its set of problems.
|
||
|
|
||
|
|
||
|
namespace mpt
|
||
|
{
|
||
|
|
||
|
|
||
|
#ifdef MPT_BUILD_FUZZER
|
||
|
static constexpr uint32 FUZZER_RNG_SEED = 3141592653u; // pi
|
||
|
#endif // MPT_BUILD_FUZZER
|
||
|
|
||
|
|
||
|
namespace detail
|
||
|
{
|
||
|
|
||
|
MPT_CONSTEXPR14_FUN int lower_bound_entropy_bits(unsigned int x)
|
||
|
{
|
||
|
if(x >= 0xffffffffu)
|
||
|
{
|
||
|
return 32;
|
||
|
} else if(x >= 0x7fffffffu)
|
||
|
{
|
||
|
return 31;
|
||
|
} else if(x >= 0x3fffffffu)
|
||
|
{
|
||
|
return 30;
|
||
|
} else if(x >= 0x1fffffffu)
|
||
|
{
|
||
|
return 29;
|
||
|
} else if(x >= 0x0fffffffu)
|
||
|
{
|
||
|
return 28;
|
||
|
} else if(x >= 0x07ffffffu)
|
||
|
{
|
||
|
return 27;
|
||
|
} else if(x >= 0x03ffffffu)
|
||
|
{
|
||
|
return 26;
|
||
|
} else if(x >= 0x01ffffffu)
|
||
|
{
|
||
|
return 25;
|
||
|
} else if(x >= 0x00ffffffu)
|
||
|
{
|
||
|
return 24;
|
||
|
} else if(x >= 0x007fffffu)
|
||
|
{
|
||
|
return 23;
|
||
|
} else if(x >= 0x003fffffu)
|
||
|
{
|
||
|
return 22;
|
||
|
} else if(x >= 0x001fffffu)
|
||
|
{
|
||
|
return 21;
|
||
|
} else if(x >= 0x000fffffu)
|
||
|
{
|
||
|
return 20;
|
||
|
} else if(x >= 0x0007ffffu)
|
||
|
{
|
||
|
return 19;
|
||
|
} else if(x >= 0x0003ffffu)
|
||
|
{
|
||
|
return 18;
|
||
|
} else if(x >= 0x0001ffffu)
|
||
|
{
|
||
|
return 17;
|
||
|
} else if(x >= 0x0000ffffu)
|
||
|
{
|
||
|
return 16;
|
||
|
} else if(x >= 0x00007fffu)
|
||
|
{
|
||
|
return 15;
|
||
|
} else if(x >= 0x00003fffu)
|
||
|
{
|
||
|
return 14;
|
||
|
} else if(x >= 0x00001fffu)
|
||
|
{
|
||
|
return 13;
|
||
|
} else if(x >= 0x00000fffu)
|
||
|
{
|
||
|
return 12;
|
||
|
} else if(x >= 0x000007ffu)
|
||
|
{
|
||
|
return 11;
|
||
|
} else if(x >= 0x000003ffu)
|
||
|
{
|
||
|
return 10;
|
||
|
} else if(x >= 0x000001ffu)
|
||
|
{
|
||
|
return 9;
|
||
|
} else if(x >= 0x000000ffu)
|
||
|
{
|
||
|
return 8;
|
||
|
} else if(x >= 0x0000007fu)
|
||
|
{
|
||
|
return 7;
|
||
|
} else if(x >= 0x0000003fu)
|
||
|
{
|
||
|
return 6;
|
||
|
} else if(x >= 0x0000001fu)
|
||
|
{
|
||
|
return 5;
|
||
|
} else if(x >= 0x0000000fu)
|
||
|
{
|
||
|
return 4;
|
||
|
} else if(x >= 0x00000007u)
|
||
|
{
|
||
|
return 3;
|
||
|
} else if(x >= 0x00000003u)
|
||
|
{
|
||
|
return 2;
|
||
|
} else if(x >= 0x00000001u)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename Trng> struct engine_traits
|
||
|
{
|
||
|
typedef typename Trng::result_type result_type;
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits()
|
||
|
{
|
||
|
return Trng::result_bits();
|
||
|
}
|
||
|
template<typename Trd>
|
||
|
static inline Trng make(Trd & rd)
|
||
|
{
|
||
|
return Trng(rd);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
template <typename T, typename Trng>
|
||
|
inline T random(Trng & rng)
|
||
|
{
|
||
|
static_assert(std::numeric_limits<T>::is_integer);
|
||
|
typedef typename std::make_unsigned<T>::type unsigned_T;
|
||
|
const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
|
||
|
unsigned_T result = 0;
|
||
|
for(std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits)
|
||
|
{
|
||
|
if constexpr(rng_bits < (sizeof(T) * 8))
|
||
|
{
|
||
|
constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
|
||
|
result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
|
||
|
} else
|
||
|
{
|
||
|
result = static_cast<unsigned_T>(rng());
|
||
|
}
|
||
|
}
|
||
|
return static_cast<T>(result);
|
||
|
}
|
||
|
|
||
|
template <typename T, std::size_t required_entropy_bits, typename Trng>
|
||
|
inline T random(Trng & rng)
|
||
|
{
|
||
|
static_assert(std::numeric_limits<T>::is_integer);
|
||
|
typedef typename std::make_unsigned<T>::type unsigned_T;
|
||
|
const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
|
||
|
unsigned_T result = 0;
|
||
|
for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits)
|
||
|
{
|
||
|
if constexpr(rng_bits < (sizeof(T) * 8))
|
||
|
{
|
||
|
constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
|
||
|
result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
|
||
|
} else
|
||
|
{
|
||
|
result = static_cast<unsigned_T>(rng());
|
||
|
}
|
||
|
}
|
||
|
if constexpr(required_entropy_bits >= (sizeof(T) * 8))
|
||
|
{
|
||
|
return static_cast<T>(result);
|
||
|
} else
|
||
|
{
|
||
|
return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename T, typename Trng>
|
||
|
inline T random(Trng & rng, std::size_t required_entropy_bits)
|
||
|
{
|
||
|
static_assert(std::numeric_limits<T>::is_integer);
|
||
|
typedef typename std::make_unsigned<T>::type unsigned_T;
|
||
|
const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
|
||
|
unsigned_T result = 0;
|
||
|
for(std::size_t entropy = 0; entropy < std::min(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits)
|
||
|
{
|
||
|
if constexpr(rng_bits < (sizeof(T) * 8))
|
||
|
{
|
||
|
constexpr unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
|
||
|
result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
|
||
|
} else
|
||
|
{
|
||
|
result = static_cast<unsigned_T>(rng());
|
||
|
}
|
||
|
}
|
||
|
if(required_entropy_bits >= (sizeof(T) * 8))
|
||
|
{
|
||
|
return static_cast<T>(result);
|
||
|
} else
|
||
|
{
|
||
|
return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename T>
|
||
|
struct uniform_real_distribution
|
||
|
{
|
||
|
private:
|
||
|
T a;
|
||
|
T b;
|
||
|
public:
|
||
|
inline uniform_real_distribution(T a, T b)
|
||
|
: a(a)
|
||
|
, b(b)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
template <typename Trng>
|
||
|
inline T operator()(Trng & rng) const
|
||
|
{
|
||
|
const int mantissa_bits = std::numeric_limits<T>::digits;
|
||
|
return ((b - a) * static_cast<T>(mpt::random<uint64, mantissa_bits>(rng)) / static_cast<T>((static_cast<uint64>(1u) << mantissa_bits))) + a;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
template <typename T, typename Trng>
|
||
|
inline T random(Trng & rng, T min, T max)
|
||
|
{
|
||
|
static_assert(!std::numeric_limits<T>::is_integer);
|
||
|
typedef mpt::uniform_real_distribution<T> dis_type;
|
||
|
dis_type dis(min, max);
|
||
|
return static_cast<T>(dis(rng));
|
||
|
}
|
||
|
|
||
|
|
||
|
namespace rng
|
||
|
{
|
||
|
|
||
|
#if MPT_COMPILER_MSVC
|
||
|
#pragma warning(push)
|
||
|
#pragma warning(disable:4724) // potential mod by 0
|
||
|
#endif // MPT_COMPILER_MSVC
|
||
|
|
||
|
template <typename Tstate, typename Tvalue, Tstate m, Tstate a, Tstate c, Tstate result_mask, int result_shift, int result_bits_>
|
||
|
class lcg
|
||
|
{
|
||
|
public:
|
||
|
typedef Tstate state_type;
|
||
|
typedef Tvalue result_type;
|
||
|
private:
|
||
|
state_type state;
|
||
|
public:
|
||
|
template <typename Trng>
|
||
|
explicit inline lcg(Trng & rd)
|
||
|
: state(mpt::random<state_type>(rd))
|
||
|
{
|
||
|
operator()(); // we return results from the current state and update state after returning. results in better pipelining.
|
||
|
}
|
||
|
explicit inline lcg(state_type seed)
|
||
|
: state(seed)
|
||
|
{
|
||
|
operator()(); // we return results from the current state and update state after returning. results in better pipelining.
|
||
|
}
|
||
|
public:
|
||
|
static MPT_CONSTEXPR11_FUN result_type min()
|
||
|
{
|
||
|
return static_cast<result_type>(0);
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN result_type max()
|
||
|
{
|
||
|
static_assert(((result_mask >> result_shift) << result_shift) == result_mask);
|
||
|
return static_cast<result_type>(result_mask >> result_shift);
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits()
|
||
|
{
|
||
|
static_assert(((static_cast<Tstate>(1) << result_bits_) - 1) == (result_mask >> result_shift));
|
||
|
return result_bits_;
|
||
|
}
|
||
|
inline result_type operator()()
|
||
|
{
|
||
|
// we return results from the current state and update state after returning. results in better pipelining.
|
||
|
state_type s = state;
|
||
|
result_type result = static_cast<result_type>((s & result_mask) >> result_shift);
|
||
|
s = Util::ModIfNotZero<state_type, m>((a * s) + c);
|
||
|
state = s;
|
||
|
return result;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
#if MPT_COMPILER_MSVC
|
||
|
#pragma warning(pop)
|
||
|
#endif // MPT_COMPILER_MSVC
|
||
|
|
||
|
typedef lcg<uint32, uint16, 0u, 214013u, 2531011u, 0x7fff0000u, 16, 15> lcg_msvc;
|
||
|
typedef lcg<uint32, uint16, 0x80000000u, 1103515245u, 12345u, 0x7fff0000u, 16, 15> lcg_c99;
|
||
|
typedef lcg<uint64, uint32, 0ull, 6364136223846793005ull, 1ull, 0xffffffff00000000ull, 32, 32> lcg_musl;
|
||
|
|
||
|
template <typename Tstate, typename Tvalue, Tstate x1, Tstate x2, Tstate x3, Tstate x4, int rol1, int rol2>
|
||
|
class modplug
|
||
|
{
|
||
|
public:
|
||
|
typedef Tstate state_type;
|
||
|
typedef Tvalue result_type;
|
||
|
private:
|
||
|
state_type state1;
|
||
|
state_type state2;
|
||
|
public:
|
||
|
template <typename Trng>
|
||
|
explicit inline modplug(Trng &rd)
|
||
|
: state1(mpt::random<state_type>(rd))
|
||
|
, state2(mpt::random<state_type>(rd))
|
||
|
{
|
||
|
}
|
||
|
explicit inline modplug(state_type seed1, state_type seed2)
|
||
|
: state1(seed1)
|
||
|
, state2(seed2)
|
||
|
{
|
||
|
}
|
||
|
public:
|
||
|
static MPT_CONSTEXPR11_FUN result_type min()
|
||
|
{
|
||
|
return static_cast<result_type>(0);
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN result_type max()
|
||
|
{
|
||
|
return std::numeric_limits<result_type>::max();
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits()
|
||
|
{
|
||
|
static_assert(std::is_integral<result_type>::value);
|
||
|
static_assert(std::is_unsigned<result_type>::value);
|
||
|
return std::numeric_limits<result_type>::digits;
|
||
|
}
|
||
|
inline result_type operator()()
|
||
|
{
|
||
|
state_type a = state1;
|
||
|
state_type b = state2;
|
||
|
a = mpt::rotl(a, rol1);
|
||
|
a ^= x1;
|
||
|
a += x2 + (b * x3);
|
||
|
b += mpt::rotl(a, rol2) * x4;
|
||
|
state1 = a;
|
||
|
state2 = b;
|
||
|
result_type result = static_cast<result_type>(b);
|
||
|
return result;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
typedef modplug<uint32, uint32, 0x10204080u, 0x78649E7Du, 4, 5, 1, 16> modplug_dither;
|
||
|
|
||
|
} // namespace rng
|
||
|
|
||
|
|
||
|
#ifdef MODPLUG_TRACKER
|
||
|
|
||
|
namespace rng
|
||
|
{
|
||
|
|
||
|
class crand
|
||
|
{
|
||
|
public:
|
||
|
typedef void state_type;
|
||
|
typedef int result_type;
|
||
|
private:
|
||
|
static void reseed(uint32 seed);
|
||
|
public:
|
||
|
template <typename Trd>
|
||
|
static void reseed(Trd & rd)
|
||
|
{
|
||
|
reseed(mpt::random<uint32>(rd));
|
||
|
}
|
||
|
public:
|
||
|
crand() { }
|
||
|
explicit crand(const std::string &) { }
|
||
|
public:
|
||
|
static MPT_CONSTEXPR11_FUN result_type min()
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN result_type max()
|
||
|
{
|
||
|
return RAND_MAX;
|
||
|
}
|
||
|
static MPT_CONSTEXPR14_FUN int result_bits()
|
||
|
{
|
||
|
return detail::lower_bound_entropy_bits(RAND_MAX);
|
||
|
}
|
||
|
result_type operator()();
|
||
|
};
|
||
|
|
||
|
} // namespace rng
|
||
|
|
||
|
#endif // MODPLUG_TRACKER
|
||
|
|
||
|
|
||
|
// C++11 std::random_device may be implemented as a deterministic PRNG.
|
||
|
// There is no way to seed this PRNG and it is allowed to be seeded with the
|
||
|
// same value on each program invocation. This makes std::random_device
|
||
|
// completely useless even as a non-cryptographic entropy pool.
|
||
|
// We fallback to time-seeded std::mt19937 if std::random_device::entropy() is
|
||
|
// 0 or less.
|
||
|
class sane_random_device
|
||
|
{
|
||
|
private:
|
||
|
mpt::mutex m;
|
||
|
std::string token;
|
||
|
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
|
||
|
std::unique_ptr<std::random_device> prd;
|
||
|
bool rd_reliable;
|
||
|
#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
|
||
|
std::unique_ptr<std::mt19937> rd_fallback;
|
||
|
public:
|
||
|
typedef unsigned int result_type;
|
||
|
private:
|
||
|
void init_fallback();
|
||
|
public:
|
||
|
sane_random_device();
|
||
|
sane_random_device(const std::string & token);
|
||
|
static MPT_CONSTEXPR11_FUN result_type min()
|
||
|
{
|
||
|
return std::numeric_limits<result_type>::min();
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN result_type max()
|
||
|
{
|
||
|
return std::numeric_limits<result_type>::max();
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits()
|
||
|
{
|
||
|
return sizeof(result_type) * 8;
|
||
|
}
|
||
|
result_type operator()();
|
||
|
};
|
||
|
|
||
|
|
||
|
template <std::size_t N>
|
||
|
class seed_seq_values
|
||
|
{
|
||
|
private:
|
||
|
unsigned int seeds[N];
|
||
|
public:
|
||
|
template <typename Trd>
|
||
|
explicit seed_seq_values(Trd & rd)
|
||
|
{
|
||
|
for(std::size_t i = 0; i < N; ++i)
|
||
|
{
|
||
|
seeds[i] = rd();
|
||
|
}
|
||
|
}
|
||
|
const unsigned int * begin() const
|
||
|
{
|
||
|
return seeds + 0;
|
||
|
}
|
||
|
const unsigned int * end() const
|
||
|
{
|
||
|
return seeds + N;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
// C++11 random does not provide any sane way to determine the amount of entropy
|
||
|
// required to seed a particular engine. VERY STUPID.
|
||
|
// List the ones we are likely to use.
|
||
|
|
||
|
template <> struct engine_traits<std::mt19937> {
|
||
|
enum : std::size_t { seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size };
|
||
|
typedef std::mt19937 rng_type;
|
||
|
typedef rng_type::result_type result_type;
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
|
||
|
template<typename Trd> static inline rng_type make(Trd & rd)
|
||
|
{
|
||
|
std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd);
|
||
|
std::seed_seq seed(values->begin(), values->end());
|
||
|
return rng_type(seed);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <> struct engine_traits<std::mt19937_64> {
|
||
|
enum : std::size_t { seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size };
|
||
|
typedef std::mt19937_64 rng_type;
|
||
|
typedef rng_type::result_type result_type;
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
|
||
|
template<typename Trd> static inline rng_type make(Trd & rd)
|
||
|
{
|
||
|
std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd);
|
||
|
std::seed_seq seed(values->begin(), values->end());
|
||
|
return rng_type(seed);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <> struct engine_traits<std::ranlux24_base> {
|
||
|
enum : std::size_t { seed_bits = std::ranlux24_base::word_size };
|
||
|
typedef std::ranlux24_base rng_type;
|
||
|
typedef rng_type::result_type result_type;
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
|
||
|
template<typename Trd> static inline rng_type make(Trd & rd)
|
||
|
{
|
||
|
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
|
||
|
std::seed_seq seed(values.begin(), values.end());
|
||
|
return rng_type(seed);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <> struct engine_traits<std::ranlux48_base> {
|
||
|
enum : std::size_t { seed_bits = std::ranlux48_base::word_size };
|
||
|
typedef std::ranlux48_base rng_type;
|
||
|
typedef rng_type::result_type result_type;
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
|
||
|
template<typename Trd> static inline rng_type make(Trd & rd)
|
||
|
{
|
||
|
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
|
||
|
std::seed_seq seed(values.begin(), values.end());
|
||
|
return rng_type(seed);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <> struct engine_traits<std::ranlux24> {
|
||
|
enum : std::size_t { seed_bits = std::ranlux24_base::word_size };
|
||
|
typedef std::ranlux24 rng_type;
|
||
|
typedef rng_type::result_type result_type;
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux24_base::word_size; }
|
||
|
template<typename Trd> static inline rng_type make(Trd & rd)
|
||
|
{
|
||
|
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
|
||
|
std::seed_seq seed(values.begin(), values.end());
|
||
|
return rng_type(seed);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <> struct engine_traits<std::ranlux48> {
|
||
|
enum : std::size_t { seed_bits = std::ranlux48_base::word_size };
|
||
|
typedef std::ranlux48 rng_type;
|
||
|
typedef rng_type::result_type result_type;
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux48_base::word_size; }
|
||
|
template<typename Trd> static inline rng_type make(Trd & rd)
|
||
|
{
|
||
|
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
|
||
|
std::seed_seq seed(values.begin(), values.end());
|
||
|
return rng_type(seed);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
class prng_random_device_seeder
|
||
|
{
|
||
|
private:
|
||
|
uint8 generate_seed8();
|
||
|
uint16 generate_seed16();
|
||
|
uint32 generate_seed32();
|
||
|
uint64 generate_seed64();
|
||
|
protected:
|
||
|
template <typename T> inline T generate_seed();
|
||
|
protected:
|
||
|
prng_random_device_seeder();
|
||
|
};
|
||
|
|
||
|
template <> inline uint8 prng_random_device_seeder::generate_seed() { return generate_seed8(); }
|
||
|
template <> inline uint16 prng_random_device_seeder::generate_seed() { return generate_seed16(); }
|
||
|
template <> inline uint32 prng_random_device_seeder::generate_seed() { return generate_seed32(); }
|
||
|
template <> inline uint64 prng_random_device_seeder::generate_seed() { return generate_seed64(); }
|
||
|
|
||
|
template <typename Trng = mpt::rng::lcg_musl>
|
||
|
class prng_random_device
|
||
|
: public prng_random_device_seeder
|
||
|
{
|
||
|
public:
|
||
|
typedef unsigned int result_type;
|
||
|
private:
|
||
|
mpt::mutex m;
|
||
|
Trng rng;
|
||
|
public:
|
||
|
prng_random_device()
|
||
|
: rng(generate_seed<typename Trng::state_type>())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
prng_random_device(const std::string &)
|
||
|
: rng(generate_seed<typename Trng::state_type>())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN result_type min()
|
||
|
{
|
||
|
return std::numeric_limits<unsigned int>::min();
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN result_type max()
|
||
|
{
|
||
|
return std::numeric_limits<unsigned int>::max();
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits()
|
||
|
{
|
||
|
return sizeof(unsigned int) * 8;
|
||
|
}
|
||
|
result_type operator()()
|
||
|
{
|
||
|
mpt::lock_guard<mpt::mutex> l(m);
|
||
|
return mpt::random<unsigned int>(rng);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
#ifdef MPT_BUILD_FUZZER
|
||
|
|
||
|
// 1. Use deterministic seeding
|
||
|
typedef mpt::prng_random_device<mpt::rng::lcg_musl> random_device;
|
||
|
|
||
|
// 2. Use fast PRNGs in order to not waste time fuzzing more complex PRNG
|
||
|
// implementations.
|
||
|
typedef mpt::rng::lcg_msvc fast_prng;
|
||
|
typedef mpt::rng::lcg_musl good_prng;
|
||
|
|
||
|
#else // !MPT_BUILD_FUZZER
|
||
|
|
||
|
// mpt::random_device always generates 32 bits of entropy
|
||
|
typedef mpt::sane_random_device random_device;
|
||
|
|
||
|
// We cannot use std::minstd_rand here because it has not a power-of-2 sized
|
||
|
// output domain which we rely upon.
|
||
|
typedef mpt::rng::lcg_msvc fast_prng; // about 3 ALU operations, ~32bit of state, suited for inner loops
|
||
|
typedef std::ranlux48 good_prng;
|
||
|
|
||
|
#endif // MPT_BUILD_FUZZER
|
||
|
|
||
|
|
||
|
typedef mpt::good_prng default_prng;
|
||
|
|
||
|
|
||
|
template <typename Trng, typename Trd>
|
||
|
inline Trng make_prng(Trd & rd)
|
||
|
{
|
||
|
return mpt::engine_traits<Trng>::make(rd);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename Trng>
|
||
|
class thread_safe_prng
|
||
|
: private Trng
|
||
|
{
|
||
|
private:
|
||
|
mpt::mutex m;
|
||
|
public:
|
||
|
typedef typename Trng::result_type result_type;
|
||
|
public:
|
||
|
template <typename Trd>
|
||
|
explicit thread_safe_prng(Trd & rd)
|
||
|
: Trng(mpt::make_prng<Trng>(rd))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
thread_safe_prng(Trng rng)
|
||
|
: Trng(rng)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
public:
|
||
|
static MPT_CONSTEXPR11_FUN typename engine_traits<Trng>::result_type min()
|
||
|
{
|
||
|
return Trng::min();
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN typename engine_traits<Trng>::result_type max()
|
||
|
{
|
||
|
return Trng::max();
|
||
|
}
|
||
|
static MPT_CONSTEXPR11_FUN int result_bits()
|
||
|
{
|
||
|
return engine_traits<Trng>::result_bits();
|
||
|
}
|
||
|
public:
|
||
|
typename engine_traits<Trng>::result_type operator()()
|
||
|
{
|
||
|
mpt::lock_guard<mpt::mutex> l(m);
|
||
|
return Trng::operator()();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
mpt::random_device & global_random_device();
|
||
|
mpt::thread_safe_prng<mpt::default_prng> & global_prng();
|
||
|
|
||
|
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT)
|
||
|
void set_global_random_device(mpt::random_device *rd);
|
||
|
void set_global_prng(mpt::thread_safe_prng<mpt::default_prng> *rng);
|
||
|
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
|
||
|
|
||
|
|
||
|
} // namespace mpt
|
||
|
|
||
|
|
||
|
OPENMPT_NAMESPACE_END
|