mirror of
https://github.com/odin-lang/Odin.git
synced 2026-01-02 03:02:37 +00:00
core/math/rand: Use core:crypto for the system RNG
This removes some code duplication and expands support for the system RNG to all targets that `core:crypto` supports.
This commit is contained in:
@@ -5,6 +5,7 @@ Package core:math/rand implements various random number generators
|
||||
package rand
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:crypto"
|
||||
import "core:math"
|
||||
import "core:mem"
|
||||
|
||||
@@ -104,27 +105,30 @@ init :: proc(r: ^Rand, seed: u64) {
|
||||
}
|
||||
|
||||
/*
|
||||
Initialises a random number generator to use the system random number generator.
|
||||
The system random number generator is platform specific.
|
||||
On `linux` refer to the `getrandom` syscall.
|
||||
On `darwin` refer to `getentropy`.
|
||||
On `windows` refer to `BCryptGenRandom`.
|
||||
|
||||
All other platforms are not supported
|
||||
Initialises a random number generator to use the system random number generator.
|
||||
The system random number generator is platform specific, and not supported
|
||||
on all targets.
|
||||
|
||||
Inputs:
|
||||
- r: The random number generator to use the system random number generator
|
||||
|
||||
WARNING: Panics if the system is not either `windows`, `darwin` or `linux`
|
||||
WARNING: Panics if the system random number generator is not supported.
|
||||
Support can be determined via the `core:crypto.HAS_RAND_BYTES` constant.
|
||||
|
||||
Example:
|
||||
import "core:crypto"
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
init_as_system_example :: proc() {
|
||||
my_rand: rand.Rand
|
||||
rand.init_as_system(&my_rand)
|
||||
fmt.println(rand.uint64(&my_rand))
|
||||
switch crypto.HAS_RAND_BYTES {
|
||||
case true:
|
||||
rand.init_as_system(&my_rand)
|
||||
fmt.println(rand.uint64(&my_rand))
|
||||
case false:
|
||||
fmt.println("system random not supported!")
|
||||
}
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
@@ -133,7 +137,7 @@ Possible Output:
|
||||
|
||||
*/
|
||||
init_as_system :: proc(r: ^Rand) {
|
||||
if !#defined(_system_random) {
|
||||
if !crypto.HAS_RAND_BYTES {
|
||||
panic(#procedure + " is not supported on this platform yet")
|
||||
}
|
||||
r.state = 0
|
||||
@@ -144,15 +148,14 @@ init_as_system :: proc(r: ^Rand) {
|
||||
@(private)
|
||||
_random_u64 :: proc(r: ^Rand) -> u64 {
|
||||
r := r
|
||||
if r == nil {
|
||||
switch {
|
||||
case r == nil:
|
||||
r = &global_rand
|
||||
case r.is_system:
|
||||
value: u64
|
||||
crypto.rand_bytes((cast([^]u8)&value)[:size_of(u64)])
|
||||
return value
|
||||
}
|
||||
when #defined(_system_random) {
|
||||
if r.is_system {
|
||||
return _system_random()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
old_state := r.state
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package rand
|
||||
|
||||
import "core:sys/darwin"
|
||||
|
||||
@(require_results)
|
||||
_system_random :: proc() -> u64 {
|
||||
for {
|
||||
value: u64
|
||||
ret := darwin.syscall_getentropy(([^]u8)(&value), size_of(value))
|
||||
if ret < 0 {
|
||||
switch ret {
|
||||
case -4: // EINTR
|
||||
continue
|
||||
case -78: // ENOSYS
|
||||
panic("getentropy not available in kernel")
|
||||
case:
|
||||
panic("getentropy failed")
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package rand
|
||||
|
||||
foreign import "odin_env"
|
||||
foreign odin_env {
|
||||
@(link_name = "rand_bytes")
|
||||
env_rand_bytes :: proc "contextless" (buf: []byte) ---
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_system_random :: proc() -> u64 {
|
||||
buf: [8]u8
|
||||
env_rand_bytes(buf[:])
|
||||
return transmute(u64)buf
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package rand
|
||||
|
||||
import "core:sys/linux"
|
||||
|
||||
@(require_results)
|
||||
_system_random :: proc() -> u64 {
|
||||
for {
|
||||
value: u64
|
||||
value_buf := (cast([^]u8)&value)[:size_of(u64)]
|
||||
_, errno := linux.getrandom(value_buf, {})
|
||||
#partial switch errno {
|
||||
case .NONE:
|
||||
// Do nothing
|
||||
case .EINTR:
|
||||
// Call interupted by a signal handler, just retry the request.
|
||||
continue
|
||||
case .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
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package rand
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
@(require_results)
|
||||
_system_random :: proc() -> u64 {
|
||||
value: u64
|
||||
status := win32.BCryptGenRandom(nil, ([^]u8)(&value), size_of(value), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
|
||||
if status < 0 {
|
||||
panic("BCryptGenRandom failed")
|
||||
}
|
||||
return value
|
||||
}
|
||||
Reference in New Issue
Block a user