]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/src/c++11/random.cc
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / c++11 / random.cc
1 // random -*- C++ -*-
2
3 // Copyright (C) 2012-2020 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24
25 #define _GLIBCXX_USE_CXX11_ABI 1
26 #define _CRT_RAND_S // define this before including <stdlib.h> to get rand_s
27
28 #include <random>
29
30 #ifdef _GLIBCXX_USE_C99_STDINT_TR1
31
32 #if defined __i386__ || defined __x86_64__
33 # include <cpuid.h>
34 # ifdef _GLIBCXX_X86_RDRAND
35 # define USE_RDRAND 1
36 # endif
37 # ifdef _GLIBCXX_X86_RDSEED
38 # define USE_RDSEED 1
39 # endif
40 #endif
41
42 #include <cerrno>
43 #include <cstdio>
44 #include <cctype> // For std::isdigit.
45
46 #if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H
47 # include <unistd.h>
48 # include <fcntl.h>
49 // Use POSIX open, close, read etc. instead of ISO fopen, fclose, fread
50 # define USE_POSIX_FILE_IO
51 #endif
52
53 #ifdef _GLIBCXX_HAVE_SYS_IOCTL_H
54 # include <sys/ioctl.h>
55 #endif
56
57 #ifdef _GLIBCXX_HAVE_LINUX_TYPES_H
58 # include <linux/types.h>
59 #endif
60
61 #ifdef _GLIBCXX_HAVE_LINUX_RANDOM_H
62 # include <linux/random.h>
63 #endif
64
65 #ifdef _GLIBCXX_USE_CRT_RAND_S
66 # include <stdlib.h>
67 #endif
68
69 #if defined USE_RDRAND || defined USE_RDSEED \
70 || defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM
71 # pragma GCC poison _M_mt
72 #else
73 // Use the mt19937 member of the union, as in previous GCC releases.
74 # define USE_MT19937 1
75 #endif
76
77 namespace std _GLIBCXX_VISIBILITY(default)
78 {
79 namespace
80 {
81 #if USE_RDRAND
82 unsigned int
83 __attribute__ ((target("rdrnd")))
84 __x86_rdrand(void*)
85 {
86 unsigned int retries = 100;
87 unsigned int val;
88
89 while (__builtin_ia32_rdrand32_step(&val) == 0)
90 if (--retries == 0)
91 std::__throw_runtime_error(__N("random_device: rdrand failed"));
92
93 return val;
94 }
95 #endif
96
97 #if USE_RDSEED
98 unsigned int
99 __attribute__ ((target("rdseed")))
100 __x86_rdseed(void*)
101 {
102 unsigned int retries = 100;
103 unsigned int val;
104
105 while (__builtin_ia32_rdseed_si_step(&val) == 0)
106 {
107 if (--retries == 0)
108 std::__throw_runtime_error(__N("random_device: rdseed failed"));
109 __builtin_ia32_pause();
110 }
111
112 return val;
113 }
114 #endif
115
116 #ifdef _GLIBCXX_USE_CRT_RAND_S
117 unsigned int
118 __winxp_rand_s(void*)
119 {
120 unsigned int val;
121 if (::rand_s(&val) != 0)
122 std::__throw_runtime_error(__N("random_device: rand_s failed"));
123 return val;
124 }
125 #endif
126 }
127
128 void
129 random_device::_M_init(const std::string& token)
130 {
131 #ifdef USE_MT19937
132 // If no real random device is supported then use the mt19937 engine.
133 _M_init_pretr1(token);
134 return;
135 #else
136
137 _M_file = nullptr;
138 _M_func = nullptr;
139 _M_fd = -1;
140
141 const char* fname [[gnu::unused]] = nullptr;
142 bool default_token [[gnu::unused]] = false;
143
144 enum { rand_s, rdseed, rdrand, device_file } which;
145
146 if (token == "default")
147 {
148 default_token = true;
149 fname = "/dev/urandom";
150 #if defined _GLIBCXX_USE_CRT_RAND_S
151 which = rand_s;
152 #elif defined USE_RDSEED
153 which = rdseed;
154 #elif defined USE_RDRAND
155 which = rdrand;
156 #elif defined _GLIBCXX_USE_DEV_RANDOM
157 which = device_file;
158 #else
159 # error "either define USE_MT19937 above or set the default device here"
160 #endif
161 }
162 #ifdef USE_RDSEED
163 else if (token == "rdseed")
164 which = rdseed;
165 #endif // USE_RDSEED
166 #ifdef USE_RDRAND
167 else if (token == "rdrand" || token == "rdrnd")
168 which = rdrand;
169 #endif // USE_RDRAND
170 #ifdef _GLIBCXX_USE_CRT_RAND_S
171 else if (token == "rand_s")
172 which = rand_s;
173 #endif // _GLIBCXX_USE_CRT_RAND_S
174 #ifdef _GLIBCXX_USE_DEV_RANDOM
175 else if (token == "/dev/urandom" || token == "/dev/random")
176 {
177 fname = token.c_str();
178 which = device_file;
179 }
180 #endif // _GLIBCXX_USE_DEV_RANDOM
181 else
182 std::__throw_runtime_error(
183 __N("random_device::random_device(const std::string&):"
184 " unsupported token"));
185
186 switch (which)
187 {
188 #ifdef _GLIBCXX_USE_CRT_RAND_S
189 case rand_s:
190 {
191 _M_func = &__winxp_rand_s;
192 return;
193 }
194 #endif // _GLIBCXX_USE_CRT_RAND_S
195 #ifdef USE_RDSEED
196 case rdseed:
197 {
198 unsigned int eax, ebx, ecx, edx;
199 // Check availability of cpuid and, for now at least, also the
200 // CPU signature for Intel and AMD.
201 if (__get_cpuid_max(0, &ebx) > 0
202 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx))
203 {
204 // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18]
205 __cpuid_count(7, 0, eax, ebx, ecx, edx);
206 if (ebx & bit_RDSEED)
207 {
208 _M_func = &__x86_rdseed;
209 return;
210 }
211 }
212 // If rdseed was explicitly requested then we're done here.
213 if (!default_token)
214 break;
215 // Otherwise fall through to try the next available option.
216 [[gnu::fallthrough]];
217 }
218 #endif // USE_RDSEED
219 #ifdef USE_RDRAND
220 case rdrand:
221 {
222 unsigned int eax, ebx, ecx, edx;
223 // Check availability of cpuid and, for now at least, also the
224 // CPU signature for Intel and AMD.
225 if (__get_cpuid_max(0, &ebx) > 0
226 && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx))
227 {
228 // CPUID.01H:ECX.RDRAND[bit 30]
229 __cpuid(1, eax, ebx, ecx, edx);
230 if (ecx & bit_RDRND)
231 {
232 _M_func = &__x86_rdrand;
233 return;
234 }
235 }
236 // If rdrand was explicitly requested then we're done here.
237 if (!default_token)
238 break;
239 // Otherwise fall through to try the next available option.
240 [[gnu::fallthrough]];
241 }
242 #endif // USE_RDRAND
243 #ifdef _GLIBCXX_USE_DEV_RANDOM
244 case device_file:
245 {
246 #ifdef USE_POSIX_FILE_IO
247 _M_fd = ::open(fname, O_RDONLY);
248 if (_M_fd != -1)
249 {
250 // Set _M_file to non-null so that _M_fini() will do clean up.
251 _M_file = &_M_fd;
252 return;
253 }
254 #else // USE_POSIX_FILE_IO
255 _M_file = static_cast<void*>(std::fopen(fname, "rb"));
256 if (_M_file)
257 return;
258 #endif // USE_POSIX_FILE_IO
259 [[gnu::fallthrough]];
260 }
261 #endif // _GLIBCXX_USE_DEV_RANDOM
262 default:
263 { }
264 }
265 std::__throw_runtime_error(
266 __N("random_device::random_device(const std::string&):"
267 " device not available"));
268 #endif // USE_MT19937
269 }
270
271 // This function is called by _M_init for targets that use mt19937 for
272 // randomness, and by code compiled against old releases of libstdc++.
273 void
274 random_device::_M_init_pretr1(const std::string& token)
275 {
276 #ifdef USE_MT19937
277 unsigned long seed = 5489UL;
278 if (token != "default" && token != "mt19937")
279 {
280 const char* nptr = token.c_str();
281 char* endptr;
282 seed = std::strtoul(nptr, &endptr, 0);
283 if (*nptr == '\0' || *endptr != '\0')
284 std::__throw_runtime_error(__N("random_device::_M_init_pretr1"
285 "(const std::string&)"));
286 }
287 _M_mt.seed(seed);
288 #else
289 // Convert old default token "mt19937" or numeric seed tokens to "default".
290 if (token == "mt19937" || std::isdigit((unsigned char)token[0]))
291 _M_init("default");
292 else
293 _M_init(token);
294 #endif
295 }
296
297 // Called by old ABI version of random_device::_M_init(const std::string&).
298 void
299 random_device::_M_init(const char* s, size_t len)
300 {
301 const std::string token(s, len);
302 #ifdef USE_MT19937
303 _M_init_pretr1(token);
304 #else
305 _M_init(token);
306 #endif
307 }
308
309 void
310 random_device::_M_fini()
311 {
312 // _M_file == nullptr means no resources to free.
313 if (!_M_file)
314 return;
315
316 #ifdef USE_POSIX_FILE_IO
317 ::close(_M_fd);
318 _M_fd = -1;
319 #else
320 std::fclose(static_cast<FILE*>(_M_file));
321 #endif
322 _M_file = nullptr;
323 }
324
325 random_device::result_type
326 random_device::_M_getval()
327 {
328 #ifdef USE_MT19937
329 return _M_mt();
330 #else
331
332 #if defined USE_RDRAND || defined USE_RDSEED || defined _GLIBCXX_USE_CRT_RAND_S
333 if (_M_func)
334 return _M_func(nullptr);
335 #endif
336
337 result_type ret;
338 void* p = &ret;
339 size_t n = sizeof(result_type);
340 #ifdef USE_POSIX_FILE_IO
341 do
342 {
343 const int e = ::read(_M_fd, p, n);
344 if (e > 0)
345 {
346 n -= e;
347 p = static_cast<char*>(p) + e;
348 }
349 else if (e != -1 || errno != EINTR)
350 __throw_runtime_error(__N("random_device could not be read"));
351 }
352 while (n > 0);
353 #else // USE_POSIX_FILE_IO
354 const size_t e = std::fread(p, n, 1, static_cast<FILE*>(_M_file));
355 if (e != 1)
356 __throw_runtime_error(__N("random_device could not be read"));
357 #endif // USE_POSIX_FILE_IO
358
359 return ret;
360 #endif // USE_MT19937
361 }
362
363 // Only called by code compiled against old releases of libstdc++.
364 // Forward the call to _M_getval() and let it decide what to do.
365 random_device::result_type
366 random_device::_M_getval_pretr1()
367 { return _M_getval(); }
368
369 double
370 random_device::_M_getentropy() const noexcept
371 {
372 #if defined _GLIBCXX_USE_DEV_RANDOM \
373 && defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT
374 if (!_M_file)
375 return 0.0;
376
377 #ifdef USE_POSIX_FILE_IO
378 const int fd = _M_fd;
379 #else
380 const int fd = ::fileno(static_cast<FILE*>(_M_file));
381 #endif
382 if (fd < 0)
383 return 0.0;
384
385 int ent;
386 if (::ioctl(fd, RNDGETENTCNT, &ent) < 0)
387 return 0.0;
388
389 if (ent < 0)
390 return 0.0;
391
392 const int max = sizeof(result_type) * __CHAR_BIT__;
393 if (ent > max)
394 ent = max;
395
396 return static_cast<double>(ent);
397 #else
398 return 0.0;
399 #endif // _GLIBCXX_USE_DEV_RANDOM && _GLIBCXX_HAVE_SYS_IOCTL_H && RNDGETENTCNT
400 }
401
402 #ifdef USE_MT19937
403 template class mersenne_twister_engine<
404 uint_fast32_t,
405 32, 624, 397, 31,
406 0x9908b0dfUL, 11,
407 0xffffffffUL, 7,
408 0x9d2c5680UL, 15,
409 0xefc60000UL, 18, 1812433253UL>;
410 #endif // USE_MT19937
411 }
412 #endif // _GLIBCXX_USE_C99_STDINT_TR1