#pragma once /** @file @brief pseudrandom generator @author MITSUNARI Shigeo(@herumi) @license modified new BSD license http://opensource.org/licenses/BSD-3-Clause */ #ifndef CYBOZU_DONT_USE_EXCEPTION #include #endif #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include #include #ifdef _MSC_VER #pragma comment (lib, "advapi32.lib") #endif #else #include #include #endif namespace cybozu { class RandomGenerator { RandomGenerator(const RandomGenerator&); void operator=(const RandomGenerator&); public: #ifdef _WIN32 RandomGenerator() : prov_(0) { DWORD flagTbl[] = { CRYPT_VERIFYCONTEXT | CRYPT_SILENT, 0, CRYPT_MACHINE_KEYSET }; for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(flagTbl); i++) { if (CryptAcquireContext(&prov_, NULL, NULL, PROV_RSA_FULL, flagTbl[i]) != 0) return; } #ifdef CYBOZU_DONT_USE_EXCEPTION prov_ = 0; #else throw cybozu::Exception("randomgenerator"); #endif } bool read_inner(void *buf, size_t byteSize) { if (prov_ == 0) return false; return CryptGenRandom(prov_, static_cast(byteSize), static_cast(buf)) != 0; } ~RandomGenerator() { if (prov_) { CryptReleaseContext(prov_, 0); } } /* fill buf[0..bufNum-1] with random data @note bufNum is not byte size */ template void read(bool *pb, T *buf, size_t bufNum) { const size_t byteSize = sizeof(T) * bufNum; *pb = read_inner(buf, byteSize); } private: HCRYPTPROV prov_; #else RandomGenerator() : fp_(::fopen("/dev/urandom", "rb")) { #ifndef CYBOZU_DONT_USE_EXCEPTION if (!fp_) throw cybozu::Exception("randomgenerator"); #endif } ~RandomGenerator() { if (fp_) ::fclose(fp_); } /* fill buf[0..bufNum-1] with random data @note bufNum is not byte size */ template void read(bool *pb, T *buf, size_t bufNum) { if (fp_ == 0) { *pb = false; return; } const size_t byteSize = sizeof(T) * bufNum; *pb = ::fread(buf, 1, (int)byteSize, fp_) == byteSize; } private: FILE *fp_; #endif #ifndef CYBOZU_DONT_USE_EXCEPTION public: template void read(T *buf, size_t bufNum) { bool b; read(&b, buf, bufNum); if (!b) throw cybozu::Exception("RandomGenerator:read") << bufNum; } uint32_t get32() { uint32_t ret; read(&ret, 1); return ret; } uint64_t get64() { uint64_t ret; read(&ret, 1); return ret; } uint32_t operator()() { return get32(); } #endif }; template void shuffle(T* v, size_t n, RG& rg) { if (n <= 1) return; for (size_t i = 0; i < n - 1; i++) { size_t r = i + size_t(rg.get64() % (n - i)); using namespace std; swap(v[i], v[r]); } } template void shuffle(V& v, RG& rg) { shuffle(v.data(), v.size(), rg); } } // cybozu