SSX3LobbyServer/lib/base/xoshiro.hpp

79 lines
2.0 KiB
C++

#pragma once
#include <climits> // CHAR_BIT
#include <cstddef>
#include <cstdint>
#include <random>
namespace base {
namespace detail {
template <class T>
constexpr size_t BitSizeOf() {
return sizeof(T) * CHAR_BIT;
}
constexpr std::uint64_t splitmix64(std::uint64_t x) {
std::uint64_t z = (x += 0x9e3779b97f4a7c15uLL);
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9uLL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebuLL;
return z ^ (z >> 31);
}
constexpr std::uint64_t rotl(std::uint64_t x, int k) {
return (x << k) | (x >> (BitSizeOf<std::uint64_t>() - k));
}
struct Xoshiro256ss {
using result_type = std::uint64_t;
std::uint64_t s[4] {};
constexpr explicit Xoshiro256ss() : Xoshiro256ss(0) {}
constexpr explicit Xoshiro256ss(std::uint64_t seed) {
s[0] = splitmix64(seed);
s[1] = splitmix64(seed);
s[2] = splitmix64(seed);
s[3] = splitmix64(seed);
}
constexpr explicit Xoshiro256ss(std::random_device& rd) {
// Get 64 bits out of the random device.
//
// This lambda is quite literal, as it fetches
// 2 iterations of the random engine, and
// shifts + OR's them into a 64bit value.
auto get_u64 = [&rd] {
std::uint64_t the_thing = rd();
return (the_thing << 32) | rd();
};
// seed with 256 bits of entropy from the random device + splitmix64
// to ensure we seed it well, as per recommendation.
s[0] = splitmix64(get_u64());
s[1] = splitmix64(get_u64());
s[2] = splitmix64(get_u64());
s[3] = splitmix64(get_u64());
}
static constexpr result_type min() { return 0; }
static constexpr result_type max() { return std::uint64_t(-1); }
constexpr result_type operator()() {
result_type result = rotl(s[1] * 5, 7) * 9;
result_type t = s[1] << 17;
s[2] ^= s[0];
s[3] ^= s[1];
s[1] ^= s[2];
s[0] ^= s[3];
s[2] ^= t;
s[3] = rotl(s[3], 45);
return result;
}
};
} // namespace detail
using detail::Xoshiro256ss;
} // namespace base