/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: RandomDevice.cpp Date: 2021-9-3 Author: Reece ***/ #include #include #include "RandomDevice.hpp" namespace Aurora::RNG { class RandomDevice : public IRandomDevice { public: void Read(void *in, AuUInt32 length) override; AuString NextString(AuUInt32 length, ERngStringCharacters type) override; void NextString(char *string, AuUInt32 length, ERngStringCharacters type) override; AuUInt8 NextByte() override; bool NextBoolean() override; AuUInt32 NextU32() override; AuUInt64 NextU64() override; AuInt32 NextInt(AuInt32 min, AuInt32 max) override; AuUInt32 NextU32(AuUInt32 min, AuUInt32 max) override; double NextDecimal() override; float NextNumber(float min, float max) override; AuUInt32 NextIndex(AuUInt32 count /* = max + 1*/) override; void Init(const RandomDef &def); private: RandomDef def_; WELLRand fast_; }; void RandomDevice::Init(const RandomDef &def) { this->def_ = def; if (!def.secure) { if (def.seed) { this->fast_ = WELL_SeedRand(def.seed.value()); } else if (def.seed64) { this->fast_ = WELL_SeedRand64(def.seed64.value()); } else if (def.seedMassive) { this->fast_ = WELL_SeedRandBig64(def.seedMassive.value()); } else { this->fast_ = {}; RNG::RngFillArray(this->fast_.state); } } // secure rng requires no init -> we just passthrough to the global ReadSecureRNG function } void RandomDevice::Read(void *in, AuUInt32 length) { if (this->def_.secure) { ReadSecureRNG(in, length); } else { WELL_NextBytes(&this->fast_, in, length); } } AuString RandomDevice::NextString(AuUInt32 length, ERngStringCharacters type) { AuString ret(length, '\00'); NextString(ret.data(), AuUInt32(ret.size()), type); return ret; } void RandomDevice::NextString(char *string, AuUInt32 length, ERngStringCharacters type) { static AuPair rngSequence[static_cast(ERngStringCharacters::eEnumCount)] = { {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 52}, {"abcdefghijklmnopqrstuvwxyz", 26}, {"ABCDEFGHIJKLMNOPQRSTUVWXYZ", 26}, {"1234567890", 10}, {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 62}, {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890=-+!$%^*.[];:", 75} }; ReadSecureRNG(string, length); 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 < length; i++) { string[i] = pair.first[reinterpret_cast(string)[i] % static_cast(pair.second)]; } } AuUInt8 RandomDevice::NextByte() { return NextFillTmpl(); } bool RandomDevice::NextBoolean() { return NextByte() > 127; } AuUInt32 RandomDevice::NextU32() { return NextFillTmpl(); } AuUInt64 RandomDevice::NextU64() { return NextFillTmpl(); } AuInt32 RandomDevice::NextInt(AuInt32 min, AuInt32 max) { auto range = max - min; return (NextU64() % range) + min; } AuUInt32 RandomDevice::NextU32(AuUInt32 min, AuUInt32 max) { auto range = max - min; return (NextU64() % range) + min; } double RandomDevice::NextDecimal() { return double(NextU32()) * (double(1.0) / double(AuNumericLimits::max())); } AuUInt32 RandomDevice::NextIndex(AuUInt32 count /* = max + 1*/) { return NextU32() % count; } float RandomDevice::NextNumber(float min, float max) { auto range = max - min; return NextDecimal() * range + min; } AUKN_SYM IRandomDevice *RandomNew(const Aurora::RNG::RandomDef &def) { auto device = _new RandomDevice(); if (!device) { return nullptr; } device->Init(def); return device; } AUKN_SYM void RandomRelease(IRandomDevice *stream) { AuSafeDelete(stream); } }