blob: 500851f9567ecf95357c492b6970b525ac71d366 [file] [log] [blame]
// 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