/* * 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 #include #ifdef MODPLUG_TRACKER #include #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 (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 struct engine_traits { typedef typename Trng::result_type result_type; static MPT_CONSTEXPR11_FUN int result_bits() { return Trng::result_bits(); } template static inline Trng make(Trd & rd) { return Trng(rd); } }; template inline T random(Trng & rng) { static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::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(rng()); } else { result = static_cast(rng()); } } return static_cast(result); } template inline T random(Trng & rng) { static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::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(rng()); } else { result = static_cast(rng()); } } if constexpr(required_entropy_bits >= (sizeof(T) * 8)) { return static_cast(result); } else { return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); } } template inline T random(Trng & rng, std::size_t required_entropy_bits) { static_assert(std::numeric_limits::is_integer); typedef typename std::make_unsigned::type unsigned_T; const unsigned int rng_bits = mpt::engine_traits::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(rng()); } else { result = static_cast(rng()); } } if(required_entropy_bits >= (sizeof(T) * 8)) { return static_cast(result); } else { return static_cast(result & ((static_cast(1) << required_entropy_bits) - static_cast(1))); } } template struct uniform_real_distribution { private: T a; T b; public: inline uniform_real_distribution(T a, T b) : a(a) , b(b) { return; } template inline T operator()(Trng & rng) const { const int mantissa_bits = std::numeric_limits::digits; return ((b - a) * static_cast(mpt::random(rng)) / static_cast((static_cast(1u) << mantissa_bits))) + a; } }; template inline T random(Trng & rng, T min, T max) { static_assert(!std::numeric_limits::is_integer); typedef mpt::uniform_real_distribution dis_type; dis_type dis(min, max); return static_cast(dis(rng)); } namespace rng { #if MPT_COMPILER_MSVC #pragma warning(push) #pragma warning(disable:4724) // potential mod by 0 #endif // MPT_COMPILER_MSVC template class lcg { public: typedef Tstate state_type; typedef Tvalue result_type; private: state_type state; public: template explicit inline lcg(Trng & rd) : state(mpt::random(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(0); } static MPT_CONSTEXPR11_FUN result_type max() { static_assert(((result_mask >> result_shift) << result_shift) == result_mask); return static_cast(result_mask >> result_shift); } static MPT_CONSTEXPR11_FUN int result_bits() { static_assert(((static_cast(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((s & result_mask) >> result_shift); s = Util::ModIfNotZero((a * s) + c); state = s; return result; } }; #if MPT_COMPILER_MSVC #pragma warning(pop) #endif // MPT_COMPILER_MSVC typedef lcg lcg_msvc; typedef lcg lcg_c99; typedef lcg lcg_musl; template class modplug { public: typedef Tstate state_type; typedef Tvalue result_type; private: state_type state1; state_type state2; public: template explicit inline modplug(Trng &rd) : state1(mpt::random(rd)) , state2(mpt::random(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(0); } static MPT_CONSTEXPR11_FUN result_type max() { return std::numeric_limits::max(); } static MPT_CONSTEXPR11_FUN int result_bits() { static_assert(std::is_integral::value); static_assert(std::is_unsigned::value); return std::numeric_limits::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(b); return result; } }; typedef modplug 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 static void reseed(Trd & rd) { reseed(mpt::random(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 prd; bool rd_reliable; #endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE std::unique_ptr 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::min(); } static MPT_CONSTEXPR11_FUN result_type max() { return std::numeric_limits::max(); } static MPT_CONSTEXPR11_FUN int result_bits() { return sizeof(result_type) * 8; } result_type operator()(); }; template class seed_seq_values { private: unsigned int seeds[N]; public: template 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 { 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 static inline rng_type make(Trd & rd) { std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } }; template <> struct engine_traits { 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 static inline rng_type make(Trd & rd) { std::unique_ptr> values = std::make_unique>(rd); std::seed_seq seed(values->begin(), values->end()); return rng_type(seed); } }; template <> struct engine_traits { 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 static inline rng_type make(Trd & rd) { mpt::seed_seq_values values(rd); std::seed_seq seed(values.begin(), values.end()); return rng_type(seed); } }; template <> struct engine_traits { 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 static inline rng_type make(Trd & rd) { mpt::seed_seq_values values(rd); std::seed_seq seed(values.begin(), values.end()); return rng_type(seed); } }; template <> struct engine_traits { 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 static inline rng_type make(Trd & rd) { mpt::seed_seq_values values(rd); std::seed_seq seed(values.begin(), values.end()); return rng_type(seed); } }; template <> struct engine_traits { 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 static inline rng_type make(Trd & rd) { mpt::seed_seq_values 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 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 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()) { return; } prng_random_device(const std::string &) : rng(generate_seed()) { return; } static MPT_CONSTEXPR11_FUN result_type min() { return std::numeric_limits::min(); } static MPT_CONSTEXPR11_FUN result_type max() { return std::numeric_limits::max(); } static MPT_CONSTEXPR11_FUN int result_bits() { return sizeof(unsigned int) * 8; } result_type operator()() { mpt::lock_guard l(m); return mpt::random(rng); } }; #ifdef MPT_BUILD_FUZZER // 1. Use deterministic seeding typedef mpt::prng_random_device 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 inline Trng make_prng(Trd & rd) { return mpt::engine_traits::make(rd); } template class thread_safe_prng : private Trng { private: mpt::mutex m; public: typedef typename Trng::result_type result_type; public: template explicit thread_safe_prng(Trd & rd) : Trng(mpt::make_prng(rd)) { return; } thread_safe_prng(Trng rng) : Trng(rng) { return; } public: static MPT_CONSTEXPR11_FUN typename engine_traits::result_type min() { return Trng::min(); } static MPT_CONSTEXPR11_FUN typename engine_traits::result_type max() { return Trng::max(); } static MPT_CONSTEXPR11_FUN int result_bits() { return engine_traits::result_bits(); } public: typename engine_traits::result_type operator()() { mpt::lock_guard l(m); return Trng::operator()(); } }; mpt::random_device & global_random_device(); mpt::thread_safe_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 *rng); #endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT } // namespace mpt OPENMPT_NAMESPACE_END