]>
Commit | Line | Data |
---|---|---|
a8c3f4c9 UD |
1 | // random -*- C++ -*- |
2 | ||
6441eb6d | 3 | // Copyright (C) 2012-2025 Free Software Foundation, Inc. |
a8c3f4c9 UD |
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 | ||
34a2b755 | 25 | #define _GLIBCXX_USE_CXX11_ABI 1 |
b0c0d878 JW |
26 | #define _CRT_RAND_S // define this before including <stdlib.h> to get rand_s |
27 | ||
a8c3f4c9 | 28 | #include <random> |
f9412ced | 29 | #include <system_error> |
a8c3f4c9 UD |
30 | |
31 | #if defined __i386__ || defined __x86_64__ | |
32 | # include <cpuid.h> | |
b0c0d878 JW |
33 | # ifdef _GLIBCXX_X86_RDRAND |
34 | # define USE_RDRAND 1 | |
35 | # endif | |
36 | # ifdef _GLIBCXX_X86_RDSEED | |
37 | # define USE_RDSEED 1 | |
38 | # endif | |
d9ebf0ce | 39 | #elif defined __powerpc64__ && defined __BUILTIN_CPU_SUPPORTS__ |
5997e6a6 | 40 | # define USE_DARN 1 |
a8c3f4c9 UD |
41 | #endif |
42 | ||
0b546684 | 43 | #include <cerrno> |
821f6f1b | 44 | #include <cstdio> |
8bc19959 | 45 | #include <cctype> // For std::isdigit. |
821f6f1b | 46 | |
b0c0d878 | 47 | #if defined _GLIBCXX_HAVE_UNISTD_H && defined _GLIBCXX_HAVE_FCNTL_H |
94e7477f | 48 | # include <unistd.h> |
b0c0d878 JW |
49 | # include <fcntl.h> |
50 | // Use POSIX open, close, read etc. instead of ISO fopen, fclose, fread | |
51 | # define USE_POSIX_FILE_IO | |
94e7477f | 52 | #endif |
a8c3f4c9 | 53 | |
78aa76df XR |
54 | #ifdef _GLIBCXX_HAVE_SYS_IOCTL_H |
55 | # include <sys/ioctl.h> | |
56 | #endif | |
57 | ||
dac867c9 UB |
58 | #ifdef _GLIBCXX_HAVE_LINUX_TYPES_H |
59 | # include <linux/types.h> | |
60 | #endif | |
61 | ||
78aa76df XR |
62 | #ifdef _GLIBCXX_HAVE_LINUX_RANDOM_H |
63 | # include <linux/random.h> | |
64 | #endif | |
65 | ||
b0c0d878 JW |
66 | #ifdef _GLIBCXX_USE_CRT_RAND_S |
67 | # include <stdlib.h> | |
68 | #endif | |
69 | ||
3439657b JW |
70 | #ifdef _GLIBCXX_HAVE_GETENTROPY |
71 | # include <unistd.h> | |
72 | #endif | |
73 | ||
74 | #if defined _GLIBCXX_USE_CRT_RAND_S || defined _GLIBCXX_USE_DEV_RANDOM \ | |
75 | || _GLIBCXX_HAVE_GETENTROPY | |
5f070ba2 | 76 | // The OS provides a source of randomness we can use. |
b0c0d878 | 77 | # pragma GCC poison _M_mt |
5997e6a6 | 78 | #elif defined USE_RDRAND || defined USE_RDSEED || defined USE_DARN |
5f070ba2 JW |
79 | // Hardware instructions might be available, but use cpuid checks at runtime. |
80 | # pragma GCC poison _M_mt | |
81 | // If the runtime cpuid checks fail we'll use a linear congruential engine. | |
82 | # define USE_LCG 1 | |
b0c0d878 JW |
83 | #else |
84 | // Use the mt19937 member of the union, as in previous GCC releases. | |
85 | # define USE_MT19937 1 | |
86 | #endif | |
87 | ||
5f070ba2 JW |
88 | #ifdef USE_LCG |
89 | # include <chrono> | |
90 | #endif | |
91 | ||
a8c3f4c9 UD |
92 | namespace std _GLIBCXX_VISIBILITY(default) |
93 | { | |
a8c3f4c9 UD |
94 | namespace |
95 | { | |
f9412ced JW |
96 | [[noreturn]] |
97 | inline void | |
98 | __throw_syserr([[maybe_unused]] int e, [[maybe_unused]] const char* msg) | |
99 | { _GLIBCXX_THROW_OR_ABORT(system_error(e, std::generic_category(), msg)); } | |
100 | ||
b0c0d878 | 101 | #if USE_RDRAND |
a8c3f4c9 UD |
102 | unsigned int |
103 | __attribute__ ((target("rdrnd"))) | |
b0c0d878 | 104 | __x86_rdrand(void*) |
a8c3f4c9 UD |
105 | { |
106 | unsigned int retries = 100; | |
107 | unsigned int val; | |
108 | ||
2627e3b7 | 109 | while (__builtin_ia32_rdrand32_step(&val) == 0) [[__unlikely__]] |
a8c3f4c9 | 110 | if (--retries == 0) |
b0c0d878 | 111 | std::__throw_runtime_error(__N("random_device: rdrand failed")); |
a8c3f4c9 UD |
112 | |
113 | return val; | |
114 | } | |
115 | #endif | |
b0c0d878 JW |
116 | |
117 | #if USE_RDSEED | |
118 | unsigned int | |
119 | __attribute__ ((target("rdseed"))) | |
a2d196e7 | 120 | __x86_rdseed(void* fallback) |
b0c0d878 JW |
121 | { |
122 | unsigned int retries = 100; | |
123 | unsigned int val; | |
124 | ||
2627e3b7 | 125 | while (__builtin_ia32_rdseed_si_step(&val) == 0) [[__unlikely__]] |
b0c0d878 JW |
126 | { |
127 | if (--retries == 0) | |
a2d196e7 JW |
128 | { |
129 | if (auto f = reinterpret_cast<unsigned int(*)(void*)>(fallback)) | |
130 | return f(nullptr); | |
131 | std::__throw_runtime_error(__N("random_device: rdseed failed")); | |
132 | } | |
b0c0d878 JW |
133 | __builtin_ia32_pause(); |
134 | } | |
135 | ||
136 | return val; | |
137 | } | |
a2d196e7 JW |
138 | |
139 | #if USE_RDRAND | |
140 | unsigned int | |
141 | __attribute__ ((target("rdseed,rdrnd"))) | |
142 | __x86_rdseed_rdrand(void*) | |
143 | { | |
144 | return __x86_rdseed(reinterpret_cast<void*>(&__x86_rdrand)); | |
145 | } | |
146 | #endif | |
b0c0d878 JW |
147 | #endif |
148 | ||
5997e6a6 JW |
149 | #ifdef USE_DARN |
150 | unsigned int | |
151 | __attribute__((target("cpu=power9"))) | |
152 | __ppc_darn(void*) | |
153 | { | |
154 | const uint64_t failed = -1; | |
155 | unsigned int retries = 10; | |
156 | uint64_t val = __builtin_darn(); | |
157 | while (val == failed) [[__unlikely__]] | |
158 | { | |
159 | if (--retries == 0) | |
160 | std::__throw_runtime_error(__N("random_device: darn failed")); | |
161 | val = __builtin_darn(); | |
162 | } | |
163 | return (uint32_t)val; | |
164 | } | |
165 | #endif | |
166 | ||
b0c0d878 JW |
167 | #ifdef _GLIBCXX_USE_CRT_RAND_S |
168 | unsigned int | |
169 | __winxp_rand_s(void*) | |
170 | { | |
171 | unsigned int val; | |
172 | if (::rand_s(&val) != 0) | |
173 | std::__throw_runtime_error(__N("random_device: rand_s failed")); | |
174 | return val; | |
175 | } | |
176 | #endif | |
5f070ba2 | 177 | |
3439657b JW |
178 | #ifdef _GLIBCXX_HAVE_GETENTROPY |
179 | unsigned int | |
180 | __libc_getentropy(void*) | |
181 | { | |
182 | unsigned int val; | |
183 | if (::getentropy(&val, sizeof(val)) != 0) | |
184 | std::__throw_runtime_error(__N("random_device: getentropy failed")); | |
185 | return val; | |
186 | } | |
187 | #endif | |
188 | ||
189 | #ifdef _GLIBCXX_HAVE_ARC4RANDOM | |
190 | unsigned int | |
191 | __libc_arc4random(void*) | |
192 | { | |
193 | return ::arc4random(); | |
194 | } | |
195 | #endif | |
196 | ||
5f070ba2 JW |
197 | #ifdef USE_LCG |
198 | // TODO: use this to seed std::mt19937 engine too. | |
199 | unsigned | |
200 | bad_seed(void* p) noexcept | |
201 | { | |
202 | // Poor quality seed based on hash of the current time and the address | |
203 | // of the object being seeded. Better than using the same default seed | |
204 | // for every object though. | |
205 | const uint64_t bits[] = { | |
206 | (uint64_t) chrono::system_clock::now().time_since_epoch().count(), | |
207 | (uint64_t) reinterpret_cast<uintptr_t>(p) | |
208 | }; | |
209 | auto bytes = reinterpret_cast<const unsigned char*>(bits); | |
210 | // 32-bit FNV-1a hash | |
211 | uint32_t h = 2166136261u; | |
212 | for (unsigned i = 0; i < sizeof(bits); ++i) | |
213 | { | |
214 | h ^= *bytes++; | |
215 | h *= 16777619u; | |
216 | } | |
217 | return h; | |
218 | } | |
219 | ||
220 | // Same as std::minstd_rand0 but using unsigned not uint_fast32_t. | |
221 | using lcg_type | |
222 | = linear_congruential_engine<unsigned, 16807UL, 0UL, 2147483647UL>; | |
223 | ||
224 | inline lcg_type* | |
225 | construct_lcg_at(void* addr) noexcept | |
226 | { | |
227 | return ::new(addr) lcg_type(bad_seed(addr)); | |
228 | } | |
229 | ||
230 | inline void | |
231 | destroy_lcg_at(void* addr) noexcept | |
232 | { | |
233 | static_cast<lcg_type*>(addr)->~lcg_type(); | |
234 | } | |
235 | ||
236 | unsigned int | |
237 | __lcg(void* ptr) noexcept | |
238 | { | |
239 | auto& lcg = *static_cast<lcg_type*>(ptr); | |
240 | return lcg(); | |
241 | } | |
242 | #endif | |
58f339fc | 243 | |
5997e6a6 | 244 | enum Which : unsigned { |
3439657b | 245 | device_file = 1, prng = 2, rand_s = 4, getentropy = 8, arc4random = 16, |
5997e6a6 | 246 | rdseed = 64, rdrand = 128, darn = 256, |
58f339fc JW |
247 | any = 0xffff |
248 | }; | |
249 | ||
5997e6a6 JW |
250 | constexpr Which |
251 | operator|(Which l, Which r) noexcept | |
252 | { return Which(unsigned(l) | unsigned(r)); } | |
253 | ||
58f339fc JW |
254 | inline Which |
255 | which_source(random_device::result_type (*func [[maybe_unused]])(void*), | |
256 | void* file [[maybe_unused]]) | |
257 | { | |
258 | #ifdef _GLIBCXX_USE_CRT_RAND_S | |
259 | if (func == &__winxp_rand_s) | |
260 | return rand_s; | |
261 | #endif | |
262 | ||
263 | #ifdef USE_RDSEED | |
264 | #ifdef USE_RDRAND | |
265 | if (func == &__x86_rdseed_rdrand) | |
266 | return rdseed; | |
267 | #endif | |
268 | if (func == &__x86_rdseed) | |
269 | return rdseed; | |
270 | #endif | |
271 | ||
272 | #ifdef USE_RDRAND | |
273 | if (func == &__x86_rdrand) | |
274 | return rdrand; | |
275 | #endif | |
276 | ||
5997e6a6 JW |
277 | #ifdef USE_DARN |
278 | if (func == &__ppc_darn) | |
279 | return darn; | |
280 | #endif | |
281 | ||
58f339fc JW |
282 | #ifdef _GLIBCXX_USE_DEV_RANDOM |
283 | if (file != nullptr) | |
284 | return device_file; | |
285 | #endif | |
286 | ||
3439657b JW |
287 | #ifdef _GLIBCXX_HAVE_ARC4RANDOM |
288 | if (func == __libc_arc4random) | |
289 | return arc4random; | |
290 | #endif | |
291 | ||
292 | #ifdef _GLIBCXX_HAVE_GETENTROPY | |
293 | if (func == __libc_getentropy) | |
294 | return getentropy; | |
295 | #endif | |
296 | ||
58f339fc JW |
297 | #ifdef USE_LCG |
298 | if (func == &__lcg) | |
299 | return prng; | |
300 | #endif | |
301 | ||
302 | #ifdef USE_MT19937 | |
303 | return prng; | |
304 | #endif | |
305 | ||
306 | return any; // should be unreachable | |
307 | } | |
a8c3f4c9 UD |
308 | } |
309 | ||
a8c3f4c9 UD |
310 | void |
311 | random_device::_M_init(const std::string& token) | |
312 | { | |
b0c0d878 JW |
313 | #ifdef USE_MT19937 |
314 | // If no real random device is supported then use the mt19937 engine. | |
315 | _M_init_pretr1(token); | |
316 | return; | |
317 | #else | |
318 | ||
319 | _M_file = nullptr; | |
320 | _M_func = nullptr; | |
321 | _M_fd = -1; | |
322 | ||
323 | const char* fname [[gnu::unused]] = nullptr; | |
b0c0d878 | 324 | |
58f339fc | 325 | Which which; |
a8c3f4c9 UD |
326 | |
327 | if (token == "default") | |
328 | { | |
5f070ba2 | 329 | which = any; |
b0c0d878 | 330 | fname = "/dev/urandom"; |
b0c0d878 JW |
331 | } |
332 | #ifdef USE_RDSEED | |
333 | else if (token == "rdseed") | |
334 | which = rdseed; | |
335 | #endif // USE_RDSEED | |
336 | #ifdef USE_RDRAND | |
337 | else if (token == "rdrand" || token == "rdrnd") | |
338 | which = rdrand; | |
339 | #endif // USE_RDRAND | |
5997e6a6 JW |
340 | #ifdef USE_DARN |
341 | else if (token == "darn") | |
342 | which = darn; | |
343 | #endif | |
344 | #if defined USE_RDRAND || defined USE_RDSEED || defined USE_DARN | |
345 | else if (token == "hw" || token == "hardware") | |
346 | which = rdrand | rdseed | darn; | |
347 | #endif | |
b0c0d878 JW |
348 | #ifdef _GLIBCXX_USE_CRT_RAND_S |
349 | else if (token == "rand_s") | |
350 | which = rand_s; | |
351 | #endif // _GLIBCXX_USE_CRT_RAND_S | |
3439657b JW |
352 | #ifdef _GLIBCXX_HAVE_GETENTROPY |
353 | else if (token == "getentropy") | |
354 | which = getentropy; | |
355 | #endif // _GLIBCXX_HAVE_GETENTROPY | |
356 | #ifdef _GLIBCXX_HAVE_ARC4RANDOM | |
357 | else if (token == "arc4random") | |
358 | which = arc4random; | |
359 | #endif // _GLIBCXX_HAVE_ARC4RANDOM | |
b0c0d878 JW |
360 | #ifdef _GLIBCXX_USE_DEV_RANDOM |
361 | else if (token == "/dev/urandom" || token == "/dev/random") | |
362 | { | |
363 | fname = token.c_str(); | |
364 | which = device_file; | |
365 | } | |
366 | #endif // _GLIBCXX_USE_DEV_RANDOM | |
5f070ba2 JW |
367 | #ifdef USE_LCG |
368 | else if (token == "prng") | |
369 | which = prng; | |
370 | #endif | |
b0c0d878 | 371 | else |
f9412ced JW |
372 | std::__throw_syserr(EINVAL, __N("random_device::random_device" |
373 | "(const std::string&):" | |
374 | " unsupported token")); | |
b0c0d878 | 375 | |
d6a6a4ea | 376 | #if defined ENOSYS |
d083c8c8 | 377 | [[maybe_unused]] const int unsupported = ENOSYS; |
d6a6a4ea | 378 | #elif defined ENOTSUP |
d083c8c8 | 379 | [[maybe_unused]] const int unsupported = ENOTSUP; |
d6a6a4ea | 380 | #else |
d083c8c8 | 381 | [[maybe_unused]] const int unsupported = 0; |
d6a6a4ea JW |
382 | #endif |
383 | int err = 0; | |
384 | ||
b0c0d878 | 385 | #ifdef _GLIBCXX_USE_CRT_RAND_S |
5f070ba2 JW |
386 | if (which & rand_s) |
387 | { | |
388 | _M_func = &__winxp_rand_s; | |
389 | return; | |
390 | } | |
b0c0d878 | 391 | #endif // _GLIBCXX_USE_CRT_RAND_S |
5f070ba2 | 392 | |
b0c0d878 | 393 | #ifdef USE_RDSEED |
5f070ba2 JW |
394 | if (which & rdseed) |
395 | { | |
396 | unsigned int eax, ebx, ecx, edx; | |
397 | // Check availability of cpuid and, for now at least, also the | |
398 | // CPU signature for Intel and AMD. | |
399 | if (__get_cpuid_max(0, &ebx) > 0 | |
400 | && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) | |
401 | { | |
402 | // CPUID.(EAX=07H, ECX=0H):EBX.RDSEED[bit 18] | |
403 | __cpuid_count(7, 0, eax, ebx, ecx, edx); | |
404 | if (ebx & bit_RDSEED) | |
405 | { | |
a2d196e7 | 406 | #ifdef USE_RDRAND |
5f070ba2 JW |
407 | // CPUID.01H:ECX.RDRAND[bit 30] |
408 | __cpuid(1, eax, ebx, ecx, edx); | |
409 | if (ecx & bit_RDRND) | |
410 | { | |
411 | _M_func = &__x86_rdseed_rdrand; | |
412 | return; | |
413 | } | |
a2d196e7 | 414 | #endif |
5f070ba2 JW |
415 | _M_func = &__x86_rdseed; |
416 | return; | |
417 | } | |
418 | } | |
d6a6a4ea | 419 | err = unsupported; |
5f070ba2 | 420 | } |
b0c0d878 | 421 | #endif // USE_RDSEED |
5f070ba2 | 422 | |
b0c0d878 | 423 | #ifdef USE_RDRAND |
5f070ba2 JW |
424 | if (which & rdrand) |
425 | { | |
426 | unsigned int eax, ebx, ecx, edx; | |
427 | // Check availability of cpuid and, for now at least, also the | |
428 | // CPU signature for Intel and AMD. | |
429 | if (__get_cpuid_max(0, &ebx) > 0 | |
430 | && (ebx == signature_INTEL_ebx || ebx == signature_AMD_ebx)) | |
431 | { | |
432 | // CPUID.01H:ECX.RDRAND[bit 30] | |
433 | __cpuid(1, eax, ebx, ecx, edx); | |
434 | if (ecx & bit_RDRND) | |
435 | { | |
436 | _M_func = &__x86_rdrand; | |
437 | return; | |
438 | } | |
439 | } | |
d6a6a4ea | 440 | err = unsupported; |
5f070ba2 | 441 | } |
b0c0d878 | 442 | #endif // USE_RDRAND |
5f070ba2 | 443 | |
5997e6a6 JW |
444 | #ifdef USE_DARN |
445 | if (which & darn) | |
446 | { | |
447 | if (__builtin_cpu_supports("darn")) | |
448 | { | |
449 | _M_func = &__ppc_darn; | |
450 | return; | |
451 | } | |
d6a6a4ea | 452 | err = unsupported; |
5997e6a6 JW |
453 | } |
454 | #endif // USE_DARN | |
455 | ||
3439657b JW |
456 | #ifdef _GLIBCXX_HAVE_ARC4RANDOM |
457 | if (which & arc4random) | |
458 | { | |
459 | _M_func = &__libc_arc4random; | |
460 | return; | |
461 | } | |
462 | #endif // _GLIBCXX_HAVE_ARC4RANDOM | |
463 | ||
464 | #ifdef _GLIBCXX_HAVE_GETENTROPY | |
465 | if (which & getentropy) | |
466 | { | |
467 | unsigned int i; | |
468 | if (::getentropy(&i, sizeof(i)) == 0) // On linux the syscall can fail. | |
469 | { | |
470 | _M_func = &__libc_getentropy; | |
471 | return; | |
472 | } | |
d6a6a4ea | 473 | err = unsupported; |
3439657b JW |
474 | } |
475 | #endif // _GLIBCXX_HAVE_GETENTROPY | |
476 | ||
b0c0d878 | 477 | #ifdef _GLIBCXX_USE_DEV_RANDOM |
5f070ba2 JW |
478 | if (which & device_file) |
479 | { | |
b0c0d878 | 480 | #ifdef USE_POSIX_FILE_IO |
5f070ba2 JW |
481 | _M_fd = ::open(fname, O_RDONLY); |
482 | if (_M_fd != -1) | |
483 | { | |
484 | // Set _M_file to non-null so that _M_fini() will do clean up. | |
485 | _M_file = &_M_fd; | |
b0c0d878 | 486 | return; |
5f070ba2 JW |
487 | } |
488 | #else // USE_POSIX_FILE_IO | |
489 | _M_file = static_cast<void*>(std::fopen(fname, "rb")); | |
490 | if (_M_file) | |
491 | return; | |
b0c0d878 | 492 | #endif // USE_POSIX_FILE_IO |
d6a6a4ea | 493 | err = errno; |
5f070ba2 | 494 | } |
b0c0d878 | 495 | #endif // _GLIBCXX_USE_DEV_RANDOM |
5f070ba2 JW |
496 | |
497 | #ifdef USE_LCG | |
498 | // Either "prng" was requested explicitly, or "default" was requested | |
499 | // but nothing above worked, use a PRNG. | |
500 | if (which & prng) | |
501 | { | |
502 | static_assert(sizeof(lcg_type) <= sizeof(_M_fd), ""); | |
503 | static_assert(alignof(lcg_type) <= alignof(_M_fd), ""); | |
504 | _M_file = construct_lcg_at(&_M_fd); | |
505 | _M_func = &__lcg; | |
506 | return; | |
b0c0d878 | 507 | } |
5f070ba2 JW |
508 | #endif |
509 | ||
d6a6a4ea JW |
510 | auto msg = __N("random_device::random_device(const std::string&):" |
511 | " device not available"); | |
512 | if (err) | |
513 | std::__throw_syserr(err, msg); | |
514 | else | |
515 | std::__throw_runtime_error(msg); | |
b0c0d878 | 516 | #endif // USE_MT19937 |
a8c3f4c9 UD |
517 | } |
518 | ||
b0c0d878 JW |
519 | // This function is called by _M_init for targets that use mt19937 for |
520 | // randomness, and by code compiled against old releases of libstdc++. | |
a8c3f4c9 UD |
521 | void |
522 | random_device::_M_init_pretr1(const std::string& token) | |
a8c3f4c9 | 523 | { |
b0c0d878 JW |
524 | #ifdef USE_MT19937 |
525 | unsigned long seed = 5489UL; | |
5f070ba2 | 526 | if (token != "default" && token != "mt19937" && token != "prng") |
b0c0d878 JW |
527 | { |
528 | const char* nptr = token.c_str(); | |
529 | char* endptr; | |
530 | seed = std::strtoul(nptr, &endptr, 0); | |
531 | if (*nptr == '\0' || *endptr != '\0') | |
f9412ced JW |
532 | std::__throw_syserr(EINVAL, __N("random_device::_M_init_pretr1" |
533 | "(const std::string&)")); | |
b0c0d878 JW |
534 | } |
535 | _M_mt.seed(seed); | |
536 | #else | |
537 | // Convert old default token "mt19937" or numeric seed tokens to "default". | |
8bc19959 | 538 | if (token == "mt19937" || std::isdigit((unsigned char)token[0])) |
b0c0d878 JW |
539 | _M_init("default"); |
540 | else | |
541 | _M_init(token); | |
542 | #endif | |
a8c3f4c9 UD |
543 | } |
544 | ||
aeedf077 JW |
545 | // Called by old ABI version of random_device::_M_init(const std::string&). |
546 | void | |
547 | random_device::_M_init(const char* s, size_t len) | |
548 | { | |
549 | const std::string token(s, len); | |
550 | #ifdef USE_MT19937 | |
551 | _M_init_pretr1(token); | |
552 | #else | |
553 | _M_init(token); | |
554 | #endif | |
555 | } | |
556 | ||
a8c3f4c9 UD |
557 | void |
558 | random_device::_M_fini() | |
559 | { | |
b0c0d878 JW |
560 | // _M_file == nullptr means no resources to free. |
561 | if (!_M_file) | |
562 | return; | |
563 | ||
5f070ba2 JW |
564 | #if USE_LCG |
565 | if (_M_func == &__lcg) | |
566 | { | |
567 | destroy_lcg_at(_M_file); | |
568 | return; | |
569 | } | |
570 | #endif | |
571 | ||
277dd6ea | 572 | #ifdef _GLIBCXX_USE_DEV_RANDOM |
b0c0d878 JW |
573 | #ifdef USE_POSIX_FILE_IO |
574 | ::close(_M_fd); | |
575 | _M_fd = -1; | |
576 | #else | |
577 | std::fclose(static_cast<FILE*>(_M_file)); | |
578 | #endif | |
579 | _M_file = nullptr; | |
277dd6ea | 580 | #endif |
a8c3f4c9 UD |
581 | } |
582 | ||
583 | random_device::result_type | |
584 | random_device::_M_getval() | |
585 | { | |
b0c0d878 JW |
586 | #ifdef USE_MT19937 |
587 | return _M_mt(); | |
588 | #else | |
589 | ||
b0c0d878 | 590 | if (_M_func) |
5f070ba2 | 591 | return _M_func(_M_file); |
a8c3f4c9 | 592 | |
b0c0d878 JW |
593 | result_type ret; |
594 | void* p = &ret; | |
a2b4d73d | 595 | size_t n = sizeof(result_type); |
b0c0d878 | 596 | #ifdef USE_POSIX_FILE_IO |
a2b4d73d JW |
597 | do |
598 | { | |
b0c0d878 | 599 | const int e = ::read(_M_fd, p, n); |
a2b4d73d JW |
600 | if (e > 0) |
601 | { | |
602 | n -= e; | |
603 | p = static_cast<char*>(p) + e; | |
604 | } | |
605 | else if (e != -1 || errno != EINTR) | |
f9412ced | 606 | __throw_syserr(errno, __N("random_device could not be read")); |
a2b4d73d JW |
607 | } |
608 | while (n > 0); | |
b0c0d878 | 609 | #else // USE_POSIX_FILE_IO |
a2b4d73d JW |
610 | const size_t e = std::fread(p, n, 1, static_cast<FILE*>(_M_file)); |
611 | if (e != 1) | |
612 | __throw_runtime_error(__N("random_device could not be read")); | |
b0c0d878 | 613 | #endif // USE_POSIX_FILE_IO |
42b6aad9 | 614 | |
b0c0d878 JW |
615 | return ret; |
616 | #endif // USE_MT19937 | |
a8c3f4c9 UD |
617 | } |
618 | ||
b0c0d878 JW |
619 | // Only called by code compiled against old releases of libstdc++. |
620 | // Forward the call to _M_getval() and let it decide what to do. | |
a8c3f4c9 UD |
621 | random_device::result_type |
622 | random_device::_M_getval_pretr1() | |
b0c0d878 | 623 | { return _M_getval(); } |
a8c3f4c9 | 624 | |
78aa76df XR |
625 | double |
626 | random_device::_M_getentropy() const noexcept | |
627 | { | |
58f339fc JW |
628 | const int max = sizeof(result_type) * __CHAR_BIT__; |
629 | ||
630 | switch(which_source(_M_func, _M_file)) | |
631 | { | |
632 | case rdrand: | |
633 | case rdseed: | |
5997e6a6 | 634 | case darn: |
58f339fc | 635 | return (double) max; |
3439657b JW |
636 | case arc4random: |
637 | case getentropy: | |
638 | return (double) max; | |
58f339fc JW |
639 | case rand_s: |
640 | case prng: | |
641 | return 0.0; | |
642 | case device_file: | |
643 | // handled below | |
644 | break; | |
645 | default: | |
646 | return 0.0; | |
647 | } | |
648 | ||
b0c0d878 JW |
649 | #if defined _GLIBCXX_USE_DEV_RANDOM \ |
650 | && defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined RNDGETENTCNT | |
78aa76df | 651 | |
b0c0d878 JW |
652 | #ifdef USE_POSIX_FILE_IO |
653 | const int fd = _M_fd; | |
654 | #else | |
655 | const int fd = ::fileno(static_cast<FILE*>(_M_file)); | |
656 | #endif | |
78aa76df XR |
657 | if (fd < 0) |
658 | return 0.0; | |
659 | ||
660 | int ent; | |
b0c0d878 | 661 | if (::ioctl(fd, RNDGETENTCNT, &ent) < 0) |
78aa76df XR |
662 | return 0.0; |
663 | ||
664 | if (ent < 0) | |
665 | return 0.0; | |
666 | ||
b6784361 JW |
667 | if (ent > max) |
668 | ent = max; | |
78aa76df XR |
669 | |
670 | return static_cast<double>(ent); | |
671 | #else | |
672 | return 0.0; | |
b0c0d878 | 673 | #endif // _GLIBCXX_USE_DEV_RANDOM && _GLIBCXX_HAVE_SYS_IOCTL_H && RNDGETENTCNT |
78aa76df XR |
674 | } |
675 | ||
b0c0d878 | 676 | #ifdef USE_MT19937 |
a8c3f4c9 UD |
677 | template class mersenne_twister_engine< |
678 | uint_fast32_t, | |
679 | 32, 624, 397, 31, | |
680 | 0x9908b0dfUL, 11, | |
681 | 0xffffffffUL, 7, | |
682 | 0x9d2c5680UL, 15, | |
683 | 0xefc60000UL, 18, 1812433253UL>; | |
b0c0d878 | 684 | #endif // USE_MT19937 |
5f070ba2 JW |
685 | |
686 | #ifdef USE_LCG | |
687 | template class | |
688 | linear_congruential_engine<unsigned, 16807UL, 0UL, 2147483647UL>; | |
689 | template struct __detail::_Mod<unsigned, 2147483647UL, 16807UL, 0UL>; | |
690 | #endif | |
a8c3f4c9 | 691 | } |