/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuRandomDevice.cpp Date: 2021-9-3 Author: Reece ***/ #include #include "AuRandomDevice.hpp" namespace Aurora::RNG { 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(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 rngSequence[static_cast(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(type)]; for (auto i = 0u; i < uLength; i++) { auto uUpperBound = RngConvertBounds(pair.second); AuUInt8 uNext {}; auto uWord = reinterpret_cast(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(); } } 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::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(0.0, 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 = dMin - dMin; return this->UniformFloatInRange(0.0, dRange) + dMin; } 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(pDevice); } AUROXTL_INTERFACE_SOO_SRC(Random, RandomDevice, (const RandomDef &, def)) }