| // Copyright Microsoft and CHERIoT Contributors. | |
| // SPDX-License-Identifier: MIT | |
| // | |
| // Imported from snmalloc 4f9d991449380ed7d881a25ba02cc5668c1ff394. | |
| #pragma once | |
| #include <cstdint> | |
| #include <cstdlib> | |
| #include <debug.hh> | |
| #include <initializer_list> | |
| namespace ds::xoroshiro | |
| { | |
| namespace detail | |
| { | |
| using Debug = ConditionalDebug<false, "xoroshiro">; | |
| /** | |
| * The xoroshiro+ (not ++) generator(s) of Blackman and Vigna's | |
| * Scrambled Linear Pseudorandom Number Generators | |
| * (https://arxiv.org/abs/1805.01407, Figure 1). | |
| * | |
| * This spelling is parameterized on the type of the two state | |
| * variables used internally (State), the type of each sample (Result), | |
| * and the seed state values (A, B, C). | |
| */ | |
| template<typename State, | |
| typename Result, | |
| State A, | |
| State B, | |
| State C, | |
| State Jump0 = 0, | |
| State Jump1 = 0, | |
| State LongJump0 = 0, | |
| State LongJump1 = 0> | |
| class XorOshiro | |
| { | |
| private: | |
| static constexpr unsigned StateBits = 8 * sizeof(State); | |
| static constexpr unsigned ResultBits = 8 * sizeof(Result); | |
| static_assert(StateBits >= ResultBits, | |
| "State must have at least as many bits as Result"); | |
| /* Parameters must be valid shifts */ | |
| static_assert(0 <= A && A <= StateBits); | |
| static_assert(0 <= B && B <= StateBits); | |
| static_assert(0 <= C && C <= StateBits); | |
| State x; | |
| State y; | |
| static inline State rotl(State x, State k) | |
| { | |
| return (x << k) | (x >> (StateBits - k)); | |
| } | |
| void jump(State jump0, State jump1) | |
| { | |
| const State Jump[] = {jump0, jump1}; | |
| uint64_t s0 = 0; | |
| uint64_t s1 = 0; | |
| for (int i : {0, 1}) | |
| { | |
| for (int b = 0; b < 64; b++) | |
| { | |
| if (Jump[i] & uint64_t(1) << b) | |
| { | |
| s0 ^= x; | |
| s1 ^= y; | |
| } | |
| next(); | |
| } | |
| } | |
| x = s0; | |
| y = s1; | |
| } | |
| public: | |
| XorOshiro(State x = 5489, State y = 0) : x(x), y(y) | |
| { | |
| // If both zero, then this does not work | |
| Debug::Invariant((x != 0) || (y != 0), | |
| "Invalid state, both x and y are zero"); | |
| next(); | |
| } | |
| void set_state(State nx, State ny = 0) | |
| { | |
| // If both zero, then this does not work | |
| Debug::Invariant((nx != 0) || (ny != 0), | |
| "Invalid state, both nx and ny are zero"); | |
| x = nx; | |
| y = ny; | |
| next(); | |
| } | |
| Result next() | |
| { | |
| State oldX = x; | |
| State oldY = y; | |
| State r = x + y; | |
| y ^= x; | |
| x = rotl(x, A) ^ y ^ (y << B); | |
| y = rotl(y, C); | |
| // If both zero, then this does not work | |
| Debug::Invariant((x != 0) || (y != 0), | |
| "Invalid state, both x and y are zero after " | |
| "next() with x: {}, y: {}", | |
| oldX, | |
| oldY); | |
| return r >> (StateBits - ResultBits); | |
| } | |
| Result operator()() | |
| { | |
| return next(); | |
| } | |
| /** | |
| * Jump. If supported, this is equivalent to 2^64 calls to next(). | |
| */ | |
| void jump() requires(Jump0 != 0) && (Jump1 != 0) | |
| { | |
| jump(Jump0, Jump1); | |
| } | |
| /** | |
| * Jump a *really* long way. If supported, this is equivalent to | |
| * 2^96 calls to next(). | |
| */ | |
| void long_jump() requires(LongJump0 != 0) && (LongJump1 != 0) | |
| { | |
| jump(LongJump0, LongJump1); | |
| } | |
| }; | |
| } // namespace detail | |
| /** | |
| * xoroshiro128+ with 64-bit output using the 2018 parameters. | |
| * | |
| * Parameters (including jump parameters) from: | |
| * https://prng.di.unimi.it/xoroshiro128plus.c | |
| */ | |
| using P128R64 = detail::XorOshiro<uint64_t, | |
| uint64_t, | |
| 24, | |
| 16, | |
| 37, | |
| 0xdf900294d8f554a5, | |
| 0x170865df4b3201fc, | |
| 0xd2a98b26625eee7b, | |
| 0xdddf9b1090aa7ac1>; | |
| /** | |
| * xoroshiro128+ with 32-bit output using the 2018 parameters. | |
| * | |
| * Parameters as per P128R64, above. | |
| */ | |
| using P128R32 = detail::XorOshiro<uint64_t, uint32_t, 24, 16, 37>; | |
| /** | |
| * A "xoroshiro64+" with 32-bit outputs. | |
| * | |
| * As per Sebastino Vigna himself, writing in | |
| * https://groups.google.com/g/prng/c/Ll-KDIbpO8k/m/bfHK4FlUCwAJ, these | |
| * parameters are one of may full-period triples. | |
| */ | |
| using P64R32 = detail::XorOshiro<uint32_t, uint32_t, 27, 7, 20>; | |
| /** | |
| * A "xoroshiro64+" with 16-bit outputs. | |
| * | |
| * Parameters as per P64R32, above. | |
| */ | |
| using P64R16 = detail::XorOshiro<uint32_t, uint16_t, 27, 7, 20>; | |
| /** | |
| * A "xoroshiro32+" with 16-bit outputs. | |
| * | |
| * Parameters as per the Parallax Propeller 2 PRNG (discarding the "++" | |
| * variant's fourth parameter "R"). See | |
| * https://forums.parallax.com/discussion/comment/1448894 . | |
| */ | |
| using P32R16 = detail::XorOshiro<uint16_t, uint16_t, 13, 5, 10>; | |
| /** | |
| * A "xoroshiro32+" with 16-bit outputs. | |
| * | |
| * Parameters as per P32R16, above. | |
| */ | |
| using P32R8 = detail::XorOshiro<uint16_t, uint8_t, 13, 5, 10>; | |
| /** | |
| * A "xoroshiro16+" with 8-bit outputs. | |
| * | |
| * As per Sebastino Vigna himself, writing in | |
| * https://groups.google.com/g/prng/c/MWJjq11zRis/m/7nM_cwRzAQAJ , the | |
| * parameters are one of may full-period triples, but "There are no good | |
| * 16-bit generators". | |
| */ | |
| using P16R8 = detail::XorOshiro<uint8_t, uint8_t, 4, 7, 3>; | |
| } // namespace ds::xoroshiro |