Browse Source

Add `runtime.default_random_generator`

gingerBill 1 year ago
parent
commit
eaec8a2bbf
2 changed files with 70 additions and 1 deletions
  1. 6 1
      base/runtime/core.odin
  2. 64 0
      base/runtime/random_generator.odin

+ 6 - 1
base/runtime/core.odin

@@ -410,8 +410,10 @@ Random_Generator_Query_Info_Flag :: enum u32 {
 }
 Random_Generator_Query_Info :: distinct bit_set[Random_Generator_Query_Info_Flag; u32]
 
+Random_Generator_Proc :: #type proc(data: rawptr, mode: Random_Generator_Mode, p: []byte)
+
 Random_Generator :: struct {
-	procedure: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte),
+	procedure: Random_Generator_Proc,
 	data:      rawptr,
 }
 
@@ -727,6 +729,9 @@ __init_context :: proc "contextless" (c: ^Context) {
 
 	c.logger.procedure = default_logger_proc
 	c.logger.data = nil
+
+	c.random_generator.procedure = default_random_generator_proc
+	c.random_generator.data = nil
 }
 
 default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {

+ 64 - 0
base/runtime/random_generator.odin

@@ -1,5 +1,7 @@
 package runtime
 
+import "base:intrinsics"
+
 @(require_results)
 random_generator_read_bytes :: proc(rg: Random_Generator, p: []byte) -> bool {
 	if rg.procedure != nil {
@@ -24,4 +26,66 @@ random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Gener
 		rg.procedure(rg.data, .Query_Info, ([^]byte)(&info)[:size_of(info)])
 	}
 	return
+}
+
+
+@(private="file")
+Default_Random_State :: struct {
+	state: u64,
+	inc:   u64,
+}
+
+default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) {
+	@(require_results)
+	read_u64 :: proc "contextless" (r: ^Default_Random_State) -> u64 {
+		old_state := r.state
+		r.state = old_state * 6364136223846793005 + (r.inc|1)
+		xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
+		rot := (old_state >> 59)
+		return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
+	}
+
+	@(thread_local)
+	global_rand_seed: Default_Random_State
+
+	switch mode {
+	case .Read:
+		r := &global_rand_seed
+
+		if r.state == 0 &&
+		   r.inc == 0 {
+		   	seed := u64(intrinsics.read_cycle_counter())
+		   	r.state = 0
+		   	r.inc = (seed << 1) | 1
+		   	_ = read_u64(r)
+		   	r.state += seed
+		   	_ = read_u64(r)
+		}
+
+		pos := i8(0)
+		val := u64(0)
+		for &v in p {
+			if pos == 0 {
+				val = read_u64(r)
+				pos = 7
+			}
+			v = byte(val)
+			val >>= 8
+			pos -= 1
+		}
+		return
+	case .Query_Info:
+		if len(p) != size_of(Random_Generator_Query_Info) {
+			return
+		}
+		info := (^Random_Generator_Query_Info)(raw_data(p))
+		info^ += {.Uniform}
+	}
+}
+
+default_random_generator :: proc "contextless" () -> Random_Generator {
+	return {
+		procedure = default_random_generator_proc,
+		data = nil,
+	}
 }