Add rand.init_as_system to allow for system-level based random number generation

This commit is contained in:
gingerBill
2022-05-15 23:43:20 +01:00
parent fdcf08410c
commit 2a58bceb56
3 changed files with 56 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ import "core:intrinsics"
Rand :: struct {
state: u64,
inc: u64,
is_system: bool,
}
@@ -29,6 +30,16 @@ init :: proc(r: ^Rand, seed: u64) {
_random(r)
}
init_as_system :: proc(r: ^Rand) {
if !#defined(_system_random) {
panic(#procedure + " is not supported on this platform yet")
}
r.state = 0
r.inc = 0
r.is_system = true
}
@(private)
_random :: proc(r: ^Rand) -> u32 {
r := r
if r == nil {
@@ -36,6 +47,12 @@ _random :: proc(r: ^Rand) -> u32 {
// enforce the global random state if necessary with `nil`
r = &global_rand
}
when #defined(_system_random) {
if r.is_system {
return _system_random()
}
}
old_state := r.state
r.state = old_state * 6364136223846793005 + (r.inc|1)
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)

View File

@@ -0,0 +1,27 @@
package rand
import "core:sys/unix"
_system_random :: proc() -> u32 {
for {
value: u32
ret := unix.sys_getrandom(([^]u8)(&value), 4, 0)
if ret < 0 {
switch ret {
case -4: // EINTR
// Call interupted by a signal handler, just retry the request.
continue
case -38: // ENOSYS
// The kernel is apparently prehistoric (< 3.17 circa 2014)
// and does not support getrandom.
panic("getrandom not available in kernel")
case:
// All other failures are things that should NEVER happen
// unless the kernel interface changes (ie: the Linux
// developers break userland).
panic("getrandom failed")
}
}
return value
}
}

View File

@@ -0,0 +1,12 @@
package rand
import win32 "core:sys/windows"
_system_random :: proc() -> u32 {
value: u32
status := win32.BCryptGenRandom(nil, ([^]u8)(&value), 4, win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
if status < 0 {
panic("BCryptGenRandom failed")
}
return value
}