aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/dexon-foundation/mcl/include/cybozu/random_generator.hpp
blob: 23096989d37cfab17b5fd079d9edab850fe73c26 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#pragma once
/**
    @file
    @brief pseudrandom generator
    @author MITSUNARI Shigeo(@herumi)
    @license modified new BSD license
    http://opensource.org/licenses/BSD-3-Clause
*/

#include <cybozu/exception.hpp>
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
#include <wincrypt.h>
#ifdef _MSC_VER
#pragma comment (lib, "advapi32.lib")
#endif
#include <cybozu/critical_section.hpp>
#else
#include <sys/types.h>
#include <fcntl.h>
#endif

namespace cybozu {

class RandomGenerator {
    RandomGenerator(const RandomGenerator&);
    void operator=(const RandomGenerator&);
public:
    uint32_t operator()()
    {
        return get32();
    }
    uint32_t get32()
    {
        uint32_t ret;
        read(&ret, 1);
        return ret;
    }
    uint64_t get64()
    {
        uint64_t ret;
        read(&ret, 1);
        return ret;
    }
#ifdef _WIN32
    RandomGenerator()
        : prov_(0)
        , pos_(bufSize)
    {
        DWORD flagTbl[] = { 0, CRYPT_NEWKEYSET };
        for (int i = 0; i < 2; i++) {
            if (CryptAcquireContext(&prov_, NULL, NULL, PROV_RSA_FULL, flagTbl[i]) != 0) return;
        }
        throw cybozu::Exception("randomgenerator");
    }
    void read_inner(void *buf, size_t byteSize)
    {
        if (CryptGenRandom(prov_, static_cast<DWORD>(byteSize), static_cast<BYTE*>(buf)) == 0) {
            throw cybozu::Exception("randomgenerator:read") << byteSize;
        }
    }
    ~RandomGenerator()
    {
        if (prov_) {
            CryptReleaseContext(prov_, 0);
        }
    }
    /*
        fill buf[0..bufNum-1] with random data
        @note bufNum is not byte size
    */
    template<class T>
    void read(T *buf, size_t bufNum)
    {
        cybozu::AutoLockCs al(cs_);
        const size_t byteSize = sizeof(T) * bufNum;
        if (byteSize > bufSize) {
            read_inner(buf, byteSize);
        } else {
            if (pos_ + byteSize > bufSize) {
                read_inner(buf_, bufSize);
                pos_ = 0;
            }
            memcpy(buf, buf_ + pos_, byteSize);
            pos_ += byteSize;
        }
    }
private:
    HCRYPTPROV prov_;
    static const size_t bufSize = 1024;
    char buf_[bufSize];
    size_t pos_;
    cybozu::CriticalSection cs_;
#else
    RandomGenerator()
        : fp_(::fopen("/dev/urandom", "rb"))
    {
        if (!fp_) throw cybozu::Exception("randomgenerator");
    }
    ~RandomGenerator()
    {
        if (fp_) ::fclose(fp_);
    }
    /*
        fill buf[0..bufNum-1] with random data
        @note bufNum is not byte size
    */
    template<class T>
    void read(T *buf, size_t bufNum)
    {
        const size_t byteSize = sizeof(T) * bufNum;
        if (::fread(buf, 1, (int)byteSize, fp_) != byteSize) {
            throw cybozu::Exception("randomgenerator:read") << byteSize;
        }
    }
#endif
private:
    FILE *fp_;
};

template<class T, class RG>
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<class V, class RG>
void shuffle(V& v, RG& rg)
{
    shuffle(v.data(), v.size(), rg);
}

} // cybozu