Browse Source

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

gingerBill 3 years ago
parent
commit
2a58bceb56
3 changed files with 56 additions and 0 deletions
  1. 17 0
      core/math/rand/rand.odin
  2. 27 0
      core/math/rand/system_linux.odin
  3. 12 0
      core/math/rand/system_windows.odin

+ 17 - 0
core/math/rand/rand.odin

@@ -5,6 +5,7 @@ import "core:intrinsics"
 Rand :: struct {
 Rand :: struct {
 	state: u64,
 	state: u64,
 	inc:   u64,
 	inc:   u64,
+	is_system: bool,
 }
 }
 
 
 
 
@@ -29,6 +30,16 @@ init :: proc(r: ^Rand, seed: u64) {
 	_random(r)
 	_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 {
 _random :: proc(r: ^Rand) -> u32 {
 	r := r
 	r := r
 	if r == nil {
 	if r == nil {
@@ -36,6 +47,12 @@ _random :: proc(r: ^Rand) -> u32 {
 		// enforce the global random state if necessary with `nil`
 		// enforce the global random state if necessary with `nil`
 		r = &global_rand
 		r = &global_rand
 	}
 	}
+	when #defined(_system_random) {
+		if r.is_system {
+			return _system_random()
+		}
+	}
+
 	old_state := r.state
 	old_state := r.state
 	r.state = old_state * 6364136223846793005 + (r.inc|1)
 	r.state = old_state * 6364136223846793005 + (r.inc|1)
 	xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)
 	xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)

+ 27 - 0
core/math/rand/system_linux.odin

@@ -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
+	}
+}

+ 12 - 0
core/math/rand/system_windows.odin

@@ -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
+}