351 lines
8.7 KiB
C++
351 lines
8.7 KiB
C++
/***
|
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: AuRandomDevice.cpp
|
|
Date: 2021-9-3
|
|
Author: Reece
|
|
***/
|
|
#include <AuroraRuntime.hpp>
|
|
#include "AuRandomDevice.hpp"
|
|
|
|
namespace Aurora::RNG
|
|
{
|
|
static const double kDblEpsilon = 2.2204460492503131e-16;
|
|
|
|
RandomDevice::RandomDevice()
|
|
{
|
|
|
|
}
|
|
|
|
RandomDevice::RandomDevice(const RandomDef &def)
|
|
{
|
|
this->Init(def);
|
|
}
|
|
|
|
void RandomDevice::Init(const RandomDef &def)
|
|
{
|
|
this->def_ = def;
|
|
|
|
// Gross...
|
|
if (!def.bSecure)
|
|
{
|
|
if (def.seed)
|
|
{
|
|
this->fast_ = AuMove(WELL_SeedRand(def.seed.value()));
|
|
}
|
|
else if (def.seed64)
|
|
{
|
|
this->fast_ = AuMove(WELL_SeedRand64(def.seed64.value()));
|
|
}
|
|
else if (def.seedMassive)
|
|
{
|
|
this->fast_ = AuMove(WELL_SeedRandBig64(def.seedMassive.value()));
|
|
}
|
|
else
|
|
{
|
|
RNG::RngFillArray<false>(this->fast_.state);
|
|
}
|
|
}
|
|
// secure rng requires no init -> we just passthrough to the global ReadSecureRNG function
|
|
}
|
|
|
|
void RandomDevice::Read(Memory::MemoryViewWrite view)
|
|
{
|
|
if (!view)
|
|
{
|
|
SysPushErrorArg();
|
|
return;
|
|
}
|
|
|
|
if (this->def_.bSecure)
|
|
{
|
|
ReadSecureRNG(view);
|
|
}
|
|
else
|
|
{
|
|
WELL_NextBytes(&this->fast_, view.ptr, AuUInt32(view.length));
|
|
}
|
|
}
|
|
|
|
AuString RandomDevice::NextString(AuUInt32 uLength, ERngStringCharacters type)
|
|
{
|
|
AuString ret(uLength, '\00');
|
|
NextString(ret.data(), AuUInt32(ret.size()), type);
|
|
return ret;
|
|
}
|
|
|
|
static AuUInt64 RngConvertBounds(AuUInt32 i)
|
|
{
|
|
AuUInt8 ret;
|
|
AuBitScanReverse(ret, i);
|
|
return 1ull << (ret + 1);
|
|
}
|
|
|
|
void RandomDevice::NextString(char *pString, AuUInt32 uLength, ERngStringCharacters type)
|
|
{
|
|
static AuPair<const char *, int> rngSequence[static_cast<int>(ERngStringCharacters::eEnumCount)] =
|
|
{
|
|
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 52},
|
|
{"abcdefghijklmnopqrstuvwxyz", 26},
|
|
{"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26},
|
|
{"1234567890", 10},
|
|
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 62},
|
|
{"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=-+!$%^*.[];:", 75}
|
|
};
|
|
|
|
if (!pString)
|
|
{
|
|
SysPushErrorArg();
|
|
return;
|
|
}
|
|
|
|
Read(AuMemoryViewWrite { pString, uLength });
|
|
|
|
if (!ERngStringCharactersIsValid(type))
|
|
{
|
|
SysPushErrorArg("NextString was called with an invalid ERngStringCharacters {}", (AuUInt)type);
|
|
type = ERngStringCharacters::eAlphaNumericCharacters; // guess we cant just return false
|
|
}
|
|
|
|
const auto &pair = rngSequence[static_cast<int>(type)];
|
|
|
|
for (auto i = 0u; i < uLength; i++)
|
|
{
|
|
auto uUpperBound = RngConvertBounds(pair.second);
|
|
AuUInt8 uNext {};
|
|
auto uWord = reinterpret_cast<const AuUInt8 *>(pString)[i];
|
|
while ((uNext = (uWord & (uUpperBound - 1))) >= pair.second)
|
|
{
|
|
uWord = AuFnv1a32Runtime(&uWord, sizeof(uWord));
|
|
}
|
|
pString[i] = pair.first[uNext];
|
|
}
|
|
}
|
|
|
|
AuUInt8 RandomDevice::NextByte()
|
|
{
|
|
if (this->def_.bSecure)
|
|
{
|
|
AuUInt8 uRet {};
|
|
ReadSecureRNG({ &uRet, 1 });
|
|
return uRet;
|
|
}
|
|
else
|
|
{
|
|
return AuUInt8(WELL_NextLong(&this->fast_) & 0xFF);
|
|
}
|
|
}
|
|
|
|
bool RandomDevice::NextBoolean()
|
|
{
|
|
if (this->def_.bSecure)
|
|
{
|
|
AuUInt8 uRet {};
|
|
ReadSecureRNG({ &uRet, 1 });
|
|
return bool(uRet & 0x1);
|
|
}
|
|
else
|
|
{
|
|
return bool(WELL_NextLong(&this->fast_) & 0x1);
|
|
}
|
|
}
|
|
|
|
AuUInt32 RandomDevice::NextU32()
|
|
{
|
|
if (this->def_.bSecure)
|
|
{
|
|
AuUInt32 uRet {};
|
|
ReadSecureRNG({ &uRet, 4 });
|
|
return uRet;
|
|
}
|
|
else
|
|
{
|
|
return WELL_NextLong(&this->fast_);
|
|
}
|
|
}
|
|
|
|
AuUInt64 RandomDevice::NextU64()
|
|
{
|
|
if (this->def_.bSecure)
|
|
{
|
|
AuUInt64 uRet {};
|
|
ReadSecureRNG({ &uRet, 8 });
|
|
return uRet;
|
|
}
|
|
else
|
|
{
|
|
return NextFillTmpl<AuUInt64>();
|
|
}
|
|
}
|
|
|
|
AuInt32 RandomDevice::NextInt(AuInt32 uMin, AuInt32 uMax)
|
|
{
|
|
auto uRange = uMax - uMin;
|
|
auto uMassiveWord = NextU32();
|
|
auto uUpperBound = RngConvertBounds(uRange + 1);
|
|
AuUInt32 uNext {};
|
|
while ((uNext = (uMassiveWord & (uUpperBound - 1))) > uRange)
|
|
{
|
|
uMassiveWord = AuFnv1a32Runtime(&uMassiveWord, sizeof(uMassiveWord));
|
|
}
|
|
return uMin + uNext;
|
|
}
|
|
|
|
AuUInt32 RandomDevice::NextU32(AuUInt32 uMin, AuUInt32 uMax)
|
|
{
|
|
auto uRange = uMax - uMin;
|
|
auto uMassiveWord = NextU32();
|
|
auto uUpperBound = RngConvertBounds(uRange + 1);
|
|
AuUInt32 uNext {};
|
|
while ((uNext = (uMassiveWord & (uUpperBound - 1))) > uRange)
|
|
{
|
|
uMassiveWord = AuFnv1a32Runtime(&uMassiveWord, sizeof(uMassiveWord));
|
|
}
|
|
return uMin + uNext;
|
|
}
|
|
|
|
// Source: https://stackoverflow.com/a/5016867
|
|
bool RandomDevice::DecGeometric(int x)
|
|
{
|
|
if (x <= 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
while (true)
|
|
{
|
|
auto r = this->NextByte();
|
|
|
|
if (x < 8)
|
|
{
|
|
return (r & ((1 << x) - 1)) == 0;
|
|
}
|
|
else if (r != 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
x -= 8;
|
|
}
|
|
}
|
|
|
|
// Source: https://stackoverflow.com/a/5016867
|
|
double RandomDevice::UniformFloatInRange(double udMin, double udMax)
|
|
{
|
|
#if defined(AURNG_USE_GARBAGE_DECIMALS)
|
|
return (double(this->NextU32()) * (double(1.0) / double(AuNumericLimits<AuUInt32>::max()))) * udMax;
|
|
#else
|
|
union
|
|
{
|
|
double f;
|
|
AuUInt64 u;
|
|
} convert;
|
|
|
|
convert.f = udMin;
|
|
auto uABits = convert.u;
|
|
convert.f = udMax;
|
|
auto uBBits = convert.u;
|
|
|
|
auto uMask = uBBits - uABits;
|
|
uMask |= uMask >> 1;
|
|
uMask |= uMask >> 2;
|
|
uMask |= uMask >> 4;
|
|
uMask |= uMask >> 8;
|
|
uMask |= uMask >> 16;
|
|
uMask |= uMask >> 32;
|
|
|
|
int bExp {};
|
|
frexp(udMax, &bExp);
|
|
|
|
while (true)
|
|
{
|
|
int iXExp {};
|
|
double x;
|
|
|
|
auto uXBits = this->NextU64();
|
|
uXBits &= uMask;
|
|
uXBits += uABits;
|
|
if (uXBits >= uBBits)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
convert.u = uXBits;
|
|
x = convert.f;
|
|
frexp(x, &iXExp);
|
|
|
|
if (DecGeometric(bExp - iXExp))
|
|
{
|
|
return x;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
double RandomDevice::NextDecimal()
|
|
{
|
|
return this->UniformFloatInRange(kDblEpsilon, 1.0);
|
|
}
|
|
|
|
AuUInt32 RandomDevice::NextIndex(AuUInt32 uCount /* = max + 1*/)
|
|
{
|
|
auto uMassiveWord = NextU32();
|
|
auto uUpperBound = RngConvertBounds(uCount);
|
|
AuUInt32 uNext {};
|
|
while ((uNext = (uMassiveWord & (uUpperBound - 1))) >= uCount)
|
|
{
|
|
uMassiveWord = AuFnv1a32Runtime(&uMassiveWord, sizeof(uMassiveWord));
|
|
}
|
|
return uNext;
|
|
}
|
|
|
|
double RandomDevice::NextNumber(double dMin, double dMax)
|
|
{
|
|
auto dRange = dMax - dMin;
|
|
return this->UniformFloatInRange(kDblEpsilon, dRange + kDblEpsilon) + dMin - kDblEpsilon;
|
|
}
|
|
|
|
uuids::uuid RandomDevice::NextUUID()
|
|
{
|
|
AuUInt8 bytes[16];
|
|
this->Read(bytes);
|
|
|
|
bytes[8] &= 0xBF;
|
|
bytes[8] |= 0x80;
|
|
|
|
bytes[6] &= 0x4F;
|
|
bytes[6] |= 0x40;
|
|
|
|
return uuids::uuid { bytes, bytes + 16 };
|
|
}
|
|
|
|
AuMemoryViewRead RandomDevice::ToSeed()
|
|
{
|
|
if (this->def_.bSecure)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
return this->fast_.state;
|
|
}
|
|
|
|
AUKN_SYM IRandomDevice *RandomNew(const Aurora::RNG::RandomDef &def)
|
|
{
|
|
auto pDevice = _new RandomDevice();
|
|
if (!pDevice)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
pDevice->Init(def);
|
|
|
|
return pDevice;
|
|
}
|
|
|
|
AUKN_SYM void RandomRelease(IRandomDevice *pDevice)
|
|
{
|
|
AuSafeDelete<RandomDevice *>(pDevice);
|
|
}
|
|
|
|
AUROXTL_INTERFACE_SOO_SRC(Random, RandomDevice, (const RandomDef &, def))
|
|
} |