Commit c1a5ff34f3
Changed files (18)
lib/std/atomic/queue.zig
@@ -242,10 +242,11 @@ test "std.atomic.Queue" {
fn startPuts(ctx: *Context) u8 {
var put_count: usize = puts_per_thread;
- var r = std.rand.DefaultPrng.init(0xdeadbeef);
+ var prng = std.rand.DefaultPrng.init(0xdeadbeef);
+ const random = prng.random();
while (put_count != 0) : (put_count -= 1) {
std.time.sleep(1); // let the os scheduler be our fuzz
- const x = @bitCast(i32, r.random.int(u32));
+ const x = @bitCast(i32, random.int(u32));
const node = ctx.allocator.create(Queue(i32).Node) catch unreachable;
node.* = .{
.prev = undefined,
lib/std/atomic/stack.zig
@@ -147,10 +147,11 @@ test "std.atomic.stack" {
fn startPuts(ctx: *Context) u8 {
var put_count: usize = puts_per_thread;
- var r = std.rand.DefaultPrng.init(0xdeadbeef);
+ var prng = std.rand.DefaultPrng.init(0xdeadbeef);
+ const random = prng.random();
while (put_count != 0) : (put_count -= 1) {
std.time.sleep(1); // let the os scheduler be our fuzz
- const x = @bitCast(i32, r.random.int(u32));
+ const x = @bitCast(i32, random.int(u32));
const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
node.* = Stack(i32).Node{
.next = undefined,
lib/std/crypto/benchmark.zig
@@ -11,6 +11,7 @@ const KiB = 1024;
const MiB = 1024 * KiB;
var prng = std.rand.DefaultPrng.init(0);
+const random = prng.random();
const Crypto = struct {
ty: type,
@@ -34,7 +35,7 @@ pub fn benchmarkHash(comptime Hash: anytype, comptime bytes: comptime_int) !u64
var h = Hash.init(.{});
var block: [Hash.digest_length]u8 = undefined;
- prng.random.bytes(block[0..]);
+ random.bytes(block[0..]);
var offset: usize = 0;
var timer = try Timer.start();
@@ -66,11 +67,11 @@ const macs = [_]Crypto{
pub fn benchmarkMac(comptime Mac: anytype, comptime bytes: comptime_int) !u64 {
var in: [512 * KiB]u8 = undefined;
- prng.random.bytes(in[0..]);
+ random.bytes(in[0..]);
const key_length = if (Mac.key_length == 0) 32 else Mac.key_length;
var key: [key_length]u8 = undefined;
- prng.random.bytes(key[0..]);
+ random.bytes(key[0..]);
var mac: [Mac.mac_length]u8 = undefined;
var offset: usize = 0;
@@ -94,10 +95,10 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c
std.debug.assert(DhKeyExchange.shared_length >= DhKeyExchange.secret_length);
var secret: [DhKeyExchange.shared_length]u8 = undefined;
- prng.random.bytes(secret[0..]);
+ random.bytes(secret[0..]);
var public: [DhKeyExchange.shared_length]u8 = undefined;
- prng.random.bytes(public[0..]);
+ random.bytes(public[0..]);
var timer = try Timer.start();
const start = timer.lap();
@@ -211,15 +212,15 @@ const aeads = [_]Crypto{
pub fn benchmarkAead(comptime Aead: anytype, comptime bytes: comptime_int) !u64 {
var in: [512 * KiB]u8 = undefined;
- prng.random.bytes(in[0..]);
+ random.bytes(in[0..]);
var tag: [Aead.tag_length]u8 = undefined;
var key: [Aead.key_length]u8 = undefined;
- prng.random.bytes(key[0..]);
+ random.bytes(key[0..]);
var nonce: [Aead.nonce_length]u8 = undefined;
- prng.random.bytes(nonce[0..]);
+ random.bytes(nonce[0..]);
var offset: usize = 0;
var timer = try Timer.start();
@@ -244,7 +245,7 @@ const aes = [_]Crypto{
pub fn benchmarkAes(comptime Aes: anytype, comptime count: comptime_int) !u64 {
var key: [Aes.key_bits / 8]u8 = undefined;
- prng.random.bytes(key[0..]);
+ random.bytes(key[0..]);
const ctx = Aes.initEnc(key);
var in = [_]u8{0} ** 16;
@@ -273,7 +274,7 @@ const aes8 = [_]Crypto{
pub fn benchmarkAes8(comptime Aes: anytype, comptime count: comptime_int) !u64 {
var key: [Aes.key_bits / 8]u8 = undefined;
- prng.random.bytes(key[0..]);
+ random.bytes(key[0..]);
const ctx = Aes.initEnc(key);
var in = [_]u8{0} ** (8 * 16);
lib/std/crypto/tlcsprng.zig
@@ -11,7 +11,10 @@ const os = std.os;
/// We use this as a layer of indirection because global const pointers cannot
/// point to thread-local variables.
-pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill };
+pub const interface = std.rand.Random{
+ .ptr = undefined,
+ .fillFn = tlsCsprngFill,
+};
const os_has_fork = switch (builtin.os.tag) {
.dragonfly,
@@ -55,7 +58,7 @@ var install_atfork_handler = std.once(struct {
threadlocal var wipe_mem: []align(mem.page_size) u8 = &[_]u8{};
-fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void {
+fn tlsCsprngFill(_: *c_void, buffer: []u8) void {
if (builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) {
// arc4random is already a thread-local CSPRNG.
return std.c.arc4random_buf(buffer.ptr, buffer.len);
lib/std/hash/benchmark.zig
@@ -11,6 +11,7 @@ const MiB = 1024 * KiB;
const GiB = 1024 * MiB;
var prng = std.rand.DefaultPrng.init(0);
+const random = prng.random();
const Hash = struct {
ty: type,
@@ -88,7 +89,7 @@ pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result {
};
var block: [block_size]u8 = undefined;
- prng.random.bytes(block[0..]);
+ random.bytes(block[0..]);
var offset: usize = 0;
var timer = try Timer.start();
@@ -110,7 +111,7 @@ pub fn benchmarkHash(comptime H: anytype, bytes: usize) !Result {
pub fn benchmarkHashSmallKeys(comptime H: anytype, key_size: usize, bytes: usize) !Result {
const key_count = bytes / key_size;
var block: [block_size]u8 = undefined;
- prng.random.bytes(block[0..]);
+ random.bytes(block[0..]);
var i: usize = 0;
var timer = try Timer.start();
lib/std/io/test.zig
@@ -20,7 +20,8 @@ test "write a file, read it, then delete it" {
var data: [1024]u8 = undefined;
var prng = DefaultPrng.init(1234);
- prng.random.bytes(data[0..]);
+ const random = prng.random();
+ random.bytes(data[0..]);
const tmp_file_name = "temp_test_file.txt";
{
var file = try tmp.dir.createFile(tmp_file_name, .{});
lib/std/math/big/rational.zig
@@ -589,9 +589,10 @@ test "big.rational set/to Float round-trip" {
var a = try Rational.init(testing.allocator);
defer a.deinit();
var prng = std.rand.DefaultPrng.init(0x5EED);
+ const random = prng.random();
var i: usize = 0;
while (i < 512) : (i += 1) {
- const r = prng.random.float(f64);
+ const r = random.float(f64);
try a.setFloat(f64, r);
try testing.expect((try a.toFloat(f64)) == r);
}
lib/std/rand/Gimli.zig
@@ -5,7 +5,6 @@ const Random = std.rand.Random;
const mem = std.mem;
const Gimli = @This();
-random: Random,
state: std.crypto.core.Gimli,
pub const secret_seed_length = 32;
@@ -16,15 +15,16 @@ pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
mem.set(u8, initial_state[secret_seed_length..], 0);
var self = Gimli{
- .random = Random{ .fillFn = fill },
.state = std.crypto.core.Gimli.init(initial_state),
};
return self;
}
-fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Gimli, "random", r);
+pub fn random(self: *Gimli) Random {
+ return Random.init(self);
+}
+pub fn fill(self: *Gimli, buf: []u8) void {
if (buf.len != 0) {
self.state.squeeze(buf);
} else {
lib/std/rand/Isaac64.zig
@@ -8,8 +8,6 @@ const Random = std.rand.Random;
const mem = std.mem;
const Isaac64 = @This();
-random: Random,
-
r: [256]u64,
m: [256]u64,
a: u64,
@@ -19,7 +17,6 @@ i: usize,
pub fn init(init_s: u64) Isaac64 {
var isaac = Isaac64{
- .random = Random{ .fillFn = fill },
.r = undefined,
.m = undefined,
.a = undefined,
@@ -33,6 +30,10 @@ pub fn init(init_s: u64) Isaac64 {
return isaac;
}
+pub fn random(self: *Isaac64) Random {
+ return Random.init(self);
+}
+
fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
const x = self.m[base + m1];
self.a = mix +% self.m[base + m2];
@@ -149,9 +150,7 @@ fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
self.i = self.r.len; // trigger refill on first value
}
-fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Isaac64, "random", r);
-
+pub fn fill(self: *Isaac64, buf: []u8) void {
var i: usize = 0;
const aligned_len = buf.len - (buf.len & 7);
@@ -230,7 +229,7 @@ test "isaac64 fill" {
var buf0: [8]u8 = undefined;
var buf1: [7]u8 = undefined;
std.mem.writeIntLittle(u64, &buf0, s);
- Isaac64.fill(&r.random, &buf1);
+ r.fill(&buf1);
try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
}
}
lib/std/rand/Pcg.zig
@@ -8,14 +8,11 @@ const Pcg = @This();
const default_multiplier = 6364136223846793005;
-random: Random,
-
s: u64,
i: u64,
pub fn init(init_s: u64) Pcg {
var pcg = Pcg{
- .random = Random{ .fillFn = fill },
.s = undefined,
.i = undefined,
};
@@ -24,6 +21,10 @@ pub fn init(init_s: u64) Pcg {
return pcg;
}
+pub fn random(self: *Pcg) Random {
+ return Random.init(self);
+}
+
fn next(self: *Pcg) u32 {
const l = self.s;
self.s = l *% default_multiplier +% (self.i | 1);
@@ -48,9 +49,7 @@ fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void {
self.s = self.s *% default_multiplier +% self.i;
}
-fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Pcg, "random", r);
-
+pub fn fill(self: *Pcg, buf: []u8) void {
var i: usize = 0;
const aligned_len = buf.len - (buf.len & 7);
@@ -113,7 +112,7 @@ test "pcg fill" {
var buf0: [4]u8 = undefined;
var buf1: [3]u8 = undefined;
std.mem.writeIntLittle(u32, &buf0, s);
- Pcg.fill(&r.random, &buf1);
+ r.fill(&buf1);
try std.testing.expect(std.mem.eql(u8, buf0[0..3], buf1[0..]));
}
}
lib/std/rand/Sfc64.zig
@@ -7,8 +7,6 @@ const Random = std.rand.Random;
const math = std.math;
const Sfc64 = @This();
-random: Random,
-
a: u64 = undefined,
b: u64 = undefined,
c: u64 = undefined,
@@ -19,14 +17,16 @@ const RightShift = 11;
const LeftShift = 3;
pub fn init(init_s: u64) Sfc64 {
- var x = Sfc64{
- .random = Random{ .fillFn = fill },
- };
+ var x = Sfc64{};
x.seed(init_s);
return x;
}
+pub fn random(self: *Sfc64) Random {
+ return Random.init(self);
+}
+
fn next(self: *Sfc64) u64 {
const tmp = self.a +% self.b +% self.counter;
self.counter += 1;
@@ -47,9 +47,7 @@ fn seed(self: *Sfc64, init_s: u64) void {
}
}
-fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Sfc64, "random", r);
-
+pub fn fill(self: *Sfc64, buf: []u8) void {
var i: usize = 0;
const aligned_len = buf.len - (buf.len & 7);
@@ -128,7 +126,7 @@ test "Sfc64 fill" {
var buf0: [8]u8 = undefined;
var buf1: [7]u8 = undefined;
std.mem.writeIntLittle(u64, &buf0, s);
- Sfc64.fill(&r.random, &buf1);
+ r.fill(&buf1);
try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
}
}
lib/std/rand/Xoroshiro128.zig
@@ -7,20 +7,19 @@ const Random = std.rand.Random;
const math = std.math;
const Xoroshiro128 = @This();
-random: Random,
-
s: [2]u64,
pub fn init(init_s: u64) Xoroshiro128 {
- var x = Xoroshiro128{
- .random = Random{ .fillFn = fill },
- .s = undefined,
- };
+ var x = Xoroshiro128{ .s = undefined };
x.seed(init_s);
return x;
}
+pub fn random(self: *Xoroshiro128) Random {
+ return Random.init(self);
+}
+
fn next(self: *Xoroshiro128) u64 {
const s0 = self.s[0];
var s1 = self.s[1];
@@ -66,9 +65,7 @@ pub fn seed(self: *Xoroshiro128, init_s: u64) void {
self.s[1] = gen.next();
}
-fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Xoroshiro128, "random", r);
-
+pub fn fill(self: *Xoroshiro128, buf: []u8) void {
var i: usize = 0;
const aligned_len = buf.len - (buf.len & 7);
@@ -144,7 +141,7 @@ test "xoroshiro fill" {
var buf0: [8]u8 = undefined;
var buf1: [7]u8 = undefined;
std.mem.writeIntLittle(u64, &buf0, s);
- Xoroshiro128.fill(&r.random, &buf1);
+ r.fill(&buf1);
try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
}
}
lib/std/rand/Xoshiro256.zig
@@ -7,13 +7,10 @@ const Random = std.rand.Random;
const math = std.math;
const Xoshiro256 = @This();
-random: Random,
-
s: [4]u64,
pub fn init(init_s: u64) Xoshiro256 {
var x = Xoshiro256{
- .random = Random{ .fillFn = fill },
.s = undefined,
};
@@ -21,6 +18,10 @@ pub fn init(init_s: u64) Xoshiro256 {
return x;
}
+pub fn random(self: *Xoshiro256) Random {
+ return Random.init(self);
+}
+
fn next(self: *Xoshiro256) u64 {
const r = math.rotl(u64, self.s[0] +% self.s[3], 23) +% self.s[0];
@@ -64,9 +65,7 @@ pub fn seed(self: *Xoshiro256, init_s: u64) void {
self.s[3] = gen.next();
}
-fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Xoshiro256, "random", r);
-
+pub fn fill(self: *Xoshiro256, buf: []u8) void {
var i: usize = 0;
const aligned_len = buf.len - (buf.len & 7);
@@ -138,7 +137,7 @@ test "xoroshiro fill" {
var buf0: [8]u8 = undefined;
var buf1: [7]u8 = undefined;
std.mem.writeIntLittle(u64, &buf0, s);
- Xoshiro256.fill(&r.random, &buf1);
+ r.fill(&buf1);
try std.testing.expect(std.mem.eql(u8, buf0[0..7], buf1[0..]));
}
}
lib/std/rand/ziggurat.zig
@@ -13,7 +13,7 @@ const builtin = @import("builtin");
const math = std.math;
const Random = std.rand.Random;
-pub fn next_f64(random: *Random, comptime tables: ZigTable) f64 {
+pub fn next_f64(random: Random, comptime tables: ZigTable) f64 {
while (true) {
// We manually construct a float from parts as we can avoid an extra random lookup here by
// using the unused exponent for the lookup table entry.
@@ -61,7 +61,7 @@ pub const ZigTable = struct {
// whether the distribution is symmetric
is_symmetric: bool,
// fallback calculation in the case we are in the 0 block
- zero_case: fn (*Random, f64) f64,
+ zero_case: fn (Random, f64) f64,
};
// zigNorInit
@@ -71,7 +71,7 @@ fn ZigTableGen(
comptime v: f64,
comptime f: fn (f64) f64,
comptime f_inv: fn (f64) f64,
- comptime zero_case: fn (*Random, f64) f64,
+ comptime zero_case: fn (Random, f64) f64,
) ZigTable {
var tables: ZigTable = undefined;
@@ -111,7 +111,7 @@ fn norm_f(x: f64) f64 {
fn norm_f_inv(y: f64) f64 {
return math.sqrt(-2.0 * math.ln(y));
}
-fn norm_zero_case(random: *Random, u: f64) f64 {
+fn norm_zero_case(random: Random, u: f64) f64 {
var x: f64 = 1;
var y: f64 = 0;
@@ -133,9 +133,11 @@ test "normal dist sanity" {
if (please_windows_dont_oom) return error.SkipZigTest;
var prng = std.rand.DefaultPrng.init(0);
+ const random = prng.random();
+
var i: usize = 0;
while (i < 1000) : (i += 1) {
- _ = prng.random.floatNorm(f64);
+ _ = random.floatNorm(f64);
}
}
@@ -154,7 +156,7 @@ fn exp_f(x: f64) f64 {
fn exp_f_inv(y: f64) f64 {
return -math.ln(y);
}
-fn exp_zero_case(random: *Random, _: f64) f64 {
+fn exp_zero_case(random: Random, _: f64) f64 {
return exp_r - math.ln(random.float(f64));
}
@@ -162,9 +164,11 @@ test "exp dist sanity" {
if (please_windows_dont_oom) return error.SkipZigTest;
var prng = std.rand.DefaultPrng.init(0);
+ const random = prng.random();
+
var i: usize = 0;
while (i < 1000) : (i += 1) {
- _ = prng.random.floatExp(f64);
+ _ = random.floatExp(f64);
}
}
lib/std/hash_map.zig
@@ -1795,10 +1795,11 @@ test "std.hash_map put and remove loop in random order" {
while (i < size) : (i += 1) {
try keys.append(i);
}
- var rng = std.rand.DefaultPrng.init(0);
+ var prng = std.rand.DefaultPrng.init(0);
+ const random = prng.random();
while (i < iterations) : (i += 1) {
- std.rand.Random.shuffle(&rng.random, u32, keys.items);
+ random.shuffle(u32, keys.items);
for (keys.items) |key| {
try map.put(key, key);
@@ -1826,14 +1827,15 @@ test "std.hash_map remove one million elements in random order" {
keys.append(i) catch unreachable;
}
- var rng = std.rand.DefaultPrng.init(0);
- std.rand.Random.shuffle(&rng.random, u32, keys.items);
+ var prng = std.rand.DefaultPrng.init(0);
+ const random = prng.random();
+ random.shuffle(u32, keys.items);
for (keys.items) |key| {
map.put(key, key) catch unreachable;
}
- std.rand.Random.shuffle(&rng.random, u32, keys.items);
+ random.shuffle(u32, keys.items);
i = 0;
while (i < n) : (i += 1) {
const key = keys.items[i];
lib/std/priority_dequeue.zig
@@ -850,17 +850,18 @@ test "std.PriorityDequeue: shrinkAndFree" {
test "std.PriorityDequeue: fuzz testing min" {
var prng = std.rand.DefaultPrng.init(0x12345678);
+ const random = prng.random();
const test_case_count = 100;
const queue_size = 1_000;
var i: usize = 0;
while (i < test_case_count) : (i += 1) {
- try fuzzTestMin(&prng.random, queue_size);
+ try fuzzTestMin(random, queue_size);
}
}
-fn fuzzTestMin(rng: *std.rand.Random, comptime queue_size: usize) !void {
+fn fuzzTestMin(rng: std.rand.Random, comptime queue_size: usize) !void {
const allocator = testing.allocator;
const items = try generateRandomSlice(allocator, rng, queue_size);
@@ -878,17 +879,18 @@ fn fuzzTestMin(rng: *std.rand.Random, comptime queue_size: usize) !void {
test "std.PriorityDequeue: fuzz testing max" {
var prng = std.rand.DefaultPrng.init(0x87654321);
+ const random = prng.random();
const test_case_count = 100;
const queue_size = 1_000;
var i: usize = 0;
while (i < test_case_count) : (i += 1) {
- try fuzzTestMax(&prng.random, queue_size);
+ try fuzzTestMax(random, queue_size);
}
}
-fn fuzzTestMax(rng: *std.rand.Random, queue_size: usize) !void {
+fn fuzzTestMax(rng: std.rand.Random, queue_size: usize) !void {
const allocator = testing.allocator;
const items = try generateRandomSlice(allocator, rng, queue_size);
@@ -906,17 +908,18 @@ fn fuzzTestMax(rng: *std.rand.Random, queue_size: usize) !void {
test "std.PriorityDequeue: fuzz testing min and max" {
var prng = std.rand.DefaultPrng.init(0x87654321);
+ const random = prng.random();
const test_case_count = 100;
const queue_size = 1_000;
var i: usize = 0;
while (i < test_case_count) : (i += 1) {
- try fuzzTestMinMax(&prng.random, queue_size);
+ try fuzzTestMinMax(random, queue_size);
}
}
-fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void {
+fn fuzzTestMinMax(rng: std.rand.Random, queue_size: usize) !void {
const allocator = testing.allocator;
const items = try generateRandomSlice(allocator, rng, queue_size);
@@ -943,7 +946,7 @@ fn fuzzTestMinMax(rng: *std.rand.Random, queue_size: usize) !void {
}
}
-fn generateRandomSlice(allocator: *std.mem.Allocator, rng: *std.rand.Random, size: usize) ![]u32 {
+fn generateRandomSlice(allocator: *std.mem.Allocator, rng: std.rand.Random, size: usize) ![]u32 {
var array = std.ArrayList(u32).init(allocator);
try array.ensureTotalCapacity(size);
lib/std/rand.zig
@@ -29,19 +29,40 @@ pub const Xoshiro256 = @import("rand/Xoshiro256.zig");
pub const Sfc64 = @import("rand/Sfc64.zig");
pub const Random = struct {
- fillFn: fn (r: *Random, buf: []u8) void,
+ ptr: *c_void,
+ fillFn: fn (ptr: *c_void, buf: []u8) void,
+
+ pub fn init(pointer: anytype) Random {
+ const Ptr = @TypeOf(pointer);
+ assert(@typeInfo(Ptr) == .Pointer); // Must be a pointer
+ assert(@typeInfo(Ptr).Pointer.size == .One); // Must be a single-item pointer
+ assert(@typeInfo(@typeInfo(Ptr).Pointer.child) == .Struct); // Must point to a struct
+ assert(std.meta.trait.hasFn("fill")(@typeInfo(Ptr).Pointer.child)); // Struct must provide the `fill` function
+ const gen = struct {
+ fn fill(ptr: *c_void, buf: []u8) void {
+ const alignment = @typeInfo(Ptr).Pointer.alignment;
+ const self = @ptrCast(Ptr, @alignCast(alignment, ptr));
+ self.fill(buf);
+ }
+ };
+
+ return .{
+ .ptr = pointer,
+ .fillFn = gen.fill,
+ };
+ }
/// Read random bytes into the specified buffer until full.
- pub fn bytes(r: *Random, buf: []u8) void {
- r.fillFn(r, buf);
+ pub fn bytes(r: Random, buf: []u8) void {
+ r.fillFn(r.ptr, buf);
}
- pub fn boolean(r: *Random) bool {
+ pub fn boolean(r: Random) bool {
return r.int(u1) != 0;
}
/// Returns a random value from an enum, evenly distributed.
- pub fn enumValue(r: *Random, comptime EnumType: type) EnumType {
+ pub fn enumValue(r: Random, comptime EnumType: type) EnumType {
if (comptime !std.meta.trait.is(.Enum)(EnumType)) {
@compileError("Random.enumValue requires an enum type, not a " ++ @typeName(EnumType));
}
@@ -55,7 +76,7 @@ pub const Random = struct {
/// Returns a random int `i` such that `minInt(T) <= i <= maxInt(T)`.
/// `i` is evenly distributed.
- pub fn int(r: *Random, comptime T: type) T {
+ pub fn int(r: Random, comptime T: type) T {
const bits = @typeInfo(T).Int.bits;
const UnsignedT = std.meta.Int(.unsigned, bits);
const ByteAlignedT = std.meta.Int(.unsigned, @divTrunc(bits + 7, 8) * 8);
@@ -73,7 +94,7 @@ pub const Random = struct {
/// Constant-time implementation off `uintLessThan`.
/// The results of this function may be biased.
- pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T {
+ pub fn uintLessThanBiased(r: Random, comptime T: type, less_than: T) T {
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
const bits = @typeInfo(T).Int.bits;
comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
@@ -93,7 +114,7 @@ pub const Random = struct {
/// However, if `fillFn` is backed by any evenly distributed pseudo random number generator,
/// this function is guaranteed to return.
/// If you need deterministic runtime bounds, use `uintLessThanBiased`.
- pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T {
+ pub fn uintLessThan(r: Random, comptime T: type, less_than: T) T {
comptime assert(@typeInfo(T).Int.signedness == .unsigned);
const bits = @typeInfo(T).Int.bits;
comptime assert(bits <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation!
@@ -130,7 +151,7 @@ pub const Random = struct {
/// Constant-time implementation off `uintAtMost`.
/// The results of this function may be biased.
- pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T {
+ pub fn uintAtMostBiased(r: Random, comptime T: type, at_most: T) T {
assert(@typeInfo(T).Int.signedness == .unsigned);
if (at_most == maxInt(T)) {
// have the full range
@@ -142,7 +163,7 @@ pub const Random = struct {
/// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`.
/// See `uintLessThan`, which this function uses in most cases,
/// for commentary on the runtime of this function.
- pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T {
+ pub fn uintAtMost(r: Random, comptime T: type, at_most: T) T {
assert(@typeInfo(T).Int.signedness == .unsigned);
if (at_most == maxInt(T)) {
// have the full range
@@ -153,7 +174,7 @@ pub const Random = struct {
/// Constant-time implementation off `intRangeLessThan`.
/// The results of this function may be biased.
- pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T {
+ pub fn intRangeLessThanBiased(r: Random, comptime T: type, at_least: T, less_than: T) T {
assert(at_least < less_than);
const info = @typeInfo(T).Int;
if (info.signedness == .signed) {
@@ -172,7 +193,7 @@ pub const Random = struct {
/// Returns an evenly distributed random integer `at_least <= i < less_than`.
/// See `uintLessThan`, which this function uses in most cases,
/// for commentary on the runtime of this function.
- pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T {
+ pub fn intRangeLessThan(r: Random, comptime T: type, at_least: T, less_than: T) T {
assert(at_least < less_than);
const info = @typeInfo(T).Int;
if (info.signedness == .signed) {
@@ -190,7 +211,7 @@ pub const Random = struct {
/// Constant-time implementation off `intRangeAtMostBiased`.
/// The results of this function may be biased.
- pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T {
+ pub fn intRangeAtMostBiased(r: Random, comptime T: type, at_least: T, at_most: T) T {
assert(at_least <= at_most);
const info = @typeInfo(T).Int;
if (info.signedness == .signed) {
@@ -209,7 +230,7 @@ pub const Random = struct {
/// Returns an evenly distributed random integer `at_least <= i <= at_most`.
/// See `uintLessThan`, which this function uses in most cases,
/// for commentary on the runtime of this function.
- pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T {
+ pub fn intRangeAtMost(r: Random, comptime T: type, at_least: T, at_most: T) T {
assert(at_least <= at_most);
const info = @typeInfo(T).Int;
if (info.signedness == .signed) {
@@ -230,7 +251,7 @@ pub const Random = struct {
pub const range = @compileError("deprecated; use intRangeLessThan()");
/// Return a floating point value evenly distributed in the range [0, 1).
- pub fn float(r: *Random, comptime T: type) T {
+ pub fn float(r: Random, comptime T: type) T {
// Generate a uniform value between [1, 2) and scale down to [0, 1).
// Note: The lowest mantissa bit is always set to 0 so we only use half the available range.
switch (T) {
@@ -251,7 +272,7 @@ pub const Random = struct {
/// Return a floating point value normally distributed with mean = 0, stddev = 1.
///
/// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean.
- pub fn floatNorm(r: *Random, comptime T: type) T {
+ pub fn floatNorm(r: Random, comptime T: type) T {
const value = ziggurat.next_f64(r, ziggurat.NormDist);
switch (T) {
f32 => return @floatCast(f32, value),
@@ -263,7 +284,7 @@ pub const Random = struct {
/// Return an exponentially distributed float with a rate parameter of 1.
///
/// To use a different rate parameter, use: floatExp(...) / desiredRate.
- pub fn floatExp(r: *Random, comptime T: type) T {
+ pub fn floatExp(r: Random, comptime T: type) T {
const value = ziggurat.next_f64(r, ziggurat.ExpDist);
switch (T) {
f32 => return @floatCast(f32, value),
@@ -273,7 +294,7 @@ pub const Random = struct {
}
/// Shuffle a slice into a random order.
- pub fn shuffle(r: *Random, comptime T: type, buf: []T) void {
+ pub fn shuffle(r: Random, comptime T: type, buf: []T) void {
if (buf.len < 2) {
return;
}
@@ -303,18 +324,19 @@ pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T {
const SequentialPrng = struct {
const Self = @This();
- random: Random,
next_value: u8,
pub fn init() Self {
return Self{
- .random = Random{ .fillFn = fill },
.next_value = 0,
};
}
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Self, "random", r);
+ pub fn random(self: *Self) Random {
+ return Random.init(self);
+ }
+
+ pub fn fill(self: *Self, buf: []u8) void {
for (buf) |*b| {
b.* = self.next_value;
}
@@ -327,45 +349,46 @@ test "Random int" {
comptime try testRandomInt();
}
fn testRandomInt() !void {
- var r = SequentialPrng.init();
-
- try expect(r.random.int(u0) == 0);
-
- r.next_value = 0;
- try expect(r.random.int(u1) == 0);
- try expect(r.random.int(u1) == 1);
- try expect(r.random.int(u2) == 2);
- try expect(r.random.int(u2) == 3);
- try expect(r.random.int(u2) == 0);
-
- r.next_value = 0xff;
- try expect(r.random.int(u8) == 0xff);
- r.next_value = 0x11;
- try expect(r.random.int(u8) == 0x11);
-
- r.next_value = 0xff;
- try expect(r.random.int(u32) == 0xffffffff);
- r.next_value = 0x11;
- try expect(r.random.int(u32) == 0x11111111);
-
- r.next_value = 0xff;
- try expect(r.random.int(i32) == -1);
- r.next_value = 0x11;
- try expect(r.random.int(i32) == 0x11111111);
-
- r.next_value = 0xff;
- try expect(r.random.int(i8) == -1);
- r.next_value = 0x11;
- try expect(r.random.int(i8) == 0x11);
-
- r.next_value = 0xff;
- try expect(r.random.int(u33) == 0x1ffffffff);
- r.next_value = 0xff;
- try expect(r.random.int(i1) == -1);
- r.next_value = 0xff;
- try expect(r.random.int(i2) == -1);
- r.next_value = 0xff;
- try expect(r.random.int(i33) == -1);
+ var rng = SequentialPrng.init();
+ const random = rng.random();
+
+ try expect(random.int(u0) == 0);
+
+ rng.next_value = 0;
+ try expect(random.int(u1) == 0);
+ try expect(random.int(u1) == 1);
+ try expect(random.int(u2) == 2);
+ try expect(random.int(u2) == 3);
+ try expect(random.int(u2) == 0);
+
+ rng.next_value = 0xff;
+ try expect(random.int(u8) == 0xff);
+ rng.next_value = 0x11;
+ try expect(random.int(u8) == 0x11);
+
+ rng.next_value = 0xff;
+ try expect(random.int(u32) == 0xffffffff);
+ rng.next_value = 0x11;
+ try expect(random.int(u32) == 0x11111111);
+
+ rng.next_value = 0xff;
+ try expect(random.int(i32) == -1);
+ rng.next_value = 0x11;
+ try expect(random.int(i32) == 0x11111111);
+
+ rng.next_value = 0xff;
+ try expect(random.int(i8) == -1);
+ rng.next_value = 0x11;
+ try expect(random.int(i8) == 0x11);
+
+ rng.next_value = 0xff;
+ try expect(random.int(u33) == 0x1ffffffff);
+ rng.next_value = 0xff;
+ try expect(random.int(i1) == -1);
+ rng.next_value = 0xff;
+ try expect(random.int(i2) == -1);
+ rng.next_value = 0xff;
+ try expect(random.int(i33) == -1);
}
test "Random boolean" {
@@ -373,11 +396,13 @@ test "Random boolean" {
comptime try testRandomBoolean();
}
fn testRandomBoolean() !void {
- var r = SequentialPrng.init();
- try expect(r.random.boolean() == false);
- try expect(r.random.boolean() == true);
- try expect(r.random.boolean() == false);
- try expect(r.random.boolean() == true);
+ var rng = SequentialPrng.init();
+ const random = rng.random();
+
+ try expect(random.boolean() == false);
+ try expect(random.boolean() == true);
+ try expect(random.boolean() == false);
+ try expect(random.boolean() == true);
}
test "Random enum" {
@@ -390,11 +415,12 @@ fn testRandomEnumValue() !void {
Second,
Third,
};
- var r = SequentialPrng.init();
- r.next_value = 0;
- try expect(r.random.enumValue(TestEnum) == TestEnum.First);
- try expect(r.random.enumValue(TestEnum) == TestEnum.First);
- try expect(r.random.enumValue(TestEnum) == TestEnum.First);
+ var rng = SequentialPrng.init();
+ const random = rng.random();
+ rng.next_value = 0;
+ try expect(random.enumValue(TestEnum) == TestEnum.First);
+ try expect(random.enumValue(TestEnum) == TestEnum.First);
+ try expect(random.enumValue(TestEnum) == TestEnum.First);
}
test "Random intLessThan" {
@@ -403,38 +429,40 @@ test "Random intLessThan" {
comptime try testRandomIntLessThan();
}
fn testRandomIntLessThan() !void {
- var r = SequentialPrng.init();
- r.next_value = 0xff;
- try expect(r.random.uintLessThan(u8, 4) == 3);
- try expect(r.next_value == 0);
- try expect(r.random.uintLessThan(u8, 4) == 0);
- try expect(r.next_value == 1);
+ var rng = SequentialPrng.init();
+ const random = rng.random();
+
+ rng.next_value = 0xff;
+ try expect(random.uintLessThan(u8, 4) == 3);
+ try expect(rng.next_value == 0);
+ try expect(random.uintLessThan(u8, 4) == 0);
+ try expect(rng.next_value == 1);
- r.next_value = 0;
- try expect(r.random.uintLessThan(u64, 32) == 0);
+ rng.next_value = 0;
+ try expect(random.uintLessThan(u64, 32) == 0);
// trigger the bias rejection code path
- r.next_value = 0;
- try expect(r.random.uintLessThan(u8, 3) == 0);
+ rng.next_value = 0;
+ try expect(random.uintLessThan(u8, 3) == 0);
// verify we incremented twice
- try expect(r.next_value == 2);
-
- r.next_value = 0xff;
- try expect(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f);
- r.next_value = 0xff;
- try expect(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe);
-
- r.next_value = 0xff;
- try expect(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f);
- r.next_value = 0xff;
- try expect(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f);
- r.next_value = 0xff;
- try expect(r.random.intRangeLessThan(i8, -0x80, 0) == -1);
-
- r.next_value = 0xff;
- try expect(r.random.intRangeLessThan(i3, -4, 0) == -1);
- r.next_value = 0xff;
- try expect(r.random.intRangeLessThan(i3, -2, 2) == 1);
+ try expect(rng.next_value == 2);
+
+ rng.next_value = 0xff;
+ try expect(random.intRangeLessThan(u8, 0, 0x80) == 0x7f);
+ rng.next_value = 0xff;
+ try expect(random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe);
+
+ rng.next_value = 0xff;
+ try expect(random.intRangeLessThan(i8, 0, 0x40) == 0x3f);
+ rng.next_value = 0xff;
+ try expect(random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f);
+ rng.next_value = 0xff;
+ try expect(random.intRangeLessThan(i8, -0x80, 0) == -1);
+
+ rng.next_value = 0xff;
+ try expect(random.intRangeLessThan(i3, -4, 0) == -1);
+ rng.next_value = 0xff;
+ try expect(random.intRangeLessThan(i3, -2, 2) == 1);
}
test "Random intAtMost" {
@@ -443,67 +471,70 @@ test "Random intAtMost" {
comptime try testRandomIntAtMost();
}
fn testRandomIntAtMost() !void {
- var r = SequentialPrng.init();
- r.next_value = 0xff;
- try expect(r.random.uintAtMost(u8, 3) == 3);
- try expect(r.next_value == 0);
- try expect(r.random.uintAtMost(u8, 3) == 0);
+ var rng = SequentialPrng.init();
+ const random = rng.random();
+
+ rng.next_value = 0xff;
+ try expect(random.uintAtMost(u8, 3) == 3);
+ try expect(rng.next_value == 0);
+ try expect(random.uintAtMost(u8, 3) == 0);
// trigger the bias rejection code path
- r.next_value = 0;
- try expect(r.random.uintAtMost(u8, 2) == 0);
+ rng.next_value = 0;
+ try expect(random.uintAtMost(u8, 2) == 0);
// verify we incremented twice
- try expect(r.next_value == 2);
-
- r.next_value = 0xff;
- try expect(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f);
- r.next_value = 0xff;
- try expect(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe);
-
- r.next_value = 0xff;
- try expect(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f);
- r.next_value = 0xff;
- try expect(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f);
- r.next_value = 0xff;
- try expect(r.random.intRangeAtMost(i8, -0x80, -1) == -1);
-
- r.next_value = 0xff;
- try expect(r.random.intRangeAtMost(i3, -4, -1) == -1);
- r.next_value = 0xff;
- try expect(r.random.intRangeAtMost(i3, -2, 1) == 1);
-
- try expect(r.random.uintAtMost(u0, 0) == 0);
+ try expect(rng.next_value == 2);
+
+ rng.next_value = 0xff;
+ try expect(random.intRangeAtMost(u8, 0, 0x7f) == 0x7f);
+ rng.next_value = 0xff;
+ try expect(random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe);
+
+ rng.next_value = 0xff;
+ try expect(random.intRangeAtMost(i8, 0, 0x3f) == 0x3f);
+ rng.next_value = 0xff;
+ try expect(random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f);
+ rng.next_value = 0xff;
+ try expect(random.intRangeAtMost(i8, -0x80, -1) == -1);
+
+ rng.next_value = 0xff;
+ try expect(random.intRangeAtMost(i3, -4, -1) == -1);
+ rng.next_value = 0xff;
+ try expect(random.intRangeAtMost(i3, -2, 1) == 1);
+
+ try expect(random.uintAtMost(u0, 0) == 0);
}
test "Random Biased" {
- var r = DefaultPrng.init(0);
+ var prng = DefaultPrng.init(0);
+ const random = prng.random();
// Not thoroughly checking the logic here.
// Just want to execute all the paths with different types.
- try expect(r.random.uintLessThanBiased(u1, 1) == 0);
- try expect(r.random.uintLessThanBiased(u32, 10) < 10);
- try expect(r.random.uintLessThanBiased(u64, 20) < 20);
+ try expect(random.uintLessThanBiased(u1, 1) == 0);
+ try expect(random.uintLessThanBiased(u32, 10) < 10);
+ try expect(random.uintLessThanBiased(u64, 20) < 20);
- try expect(r.random.uintAtMostBiased(u0, 0) == 0);
- try expect(r.random.uintAtMostBiased(u1, 0) <= 0);
- try expect(r.random.uintAtMostBiased(u32, 10) <= 10);
- try expect(r.random.uintAtMostBiased(u64, 20) <= 20);
+ try expect(random.uintAtMostBiased(u0, 0) == 0);
+ try expect(random.uintAtMostBiased(u1, 0) <= 0);
+ try expect(random.uintAtMostBiased(u32, 10) <= 10);
+ try expect(random.uintAtMostBiased(u64, 20) <= 20);
- try expect(r.random.intRangeLessThanBiased(u1, 0, 1) == 0);
- try expect(r.random.intRangeLessThanBiased(i1, -1, 0) == -1);
- try expect(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10);
- try expect(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10);
- try expect(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20);
- try expect(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20);
+ try expect(random.intRangeLessThanBiased(u1, 0, 1) == 0);
+ try expect(random.intRangeLessThanBiased(i1, -1, 0) == -1);
+ try expect(random.intRangeLessThanBiased(u32, 10, 20) >= 10);
+ try expect(random.intRangeLessThanBiased(i32, 10, 20) >= 10);
+ try expect(random.intRangeLessThanBiased(u64, 20, 40) >= 20);
+ try expect(random.intRangeLessThanBiased(i64, 20, 40) >= 20);
// uncomment for broken module error:
- //expect(r.random.intRangeAtMostBiased(u0, 0, 0) == 0);
- try expect(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0);
- try expect(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1);
- try expect(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10);
- try expect(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10);
- try expect(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20);
- try expect(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20);
+ //expect(random.intRangeAtMostBiased(u0, 0, 0) == 0);
+ try expect(random.intRangeAtMostBiased(u1, 0, 1) >= 0);
+ try expect(random.intRangeAtMostBiased(i1, -1, 0) >= -1);
+ try expect(random.intRangeAtMostBiased(u32, 10, 20) >= 10);
+ try expect(random.intRangeAtMostBiased(i32, 10, 20) >= 10);
+ try expect(random.intRangeAtMostBiased(u64, 20, 40) >= 20);
+ try expect(random.intRangeAtMostBiased(i64, 20, 40) >= 20);
}
// Generator to extend 64-bit seed values into longer sequences.
@@ -547,14 +578,15 @@ test "splitmix64 sequence" {
// Actual Random helper function tests, pcg engine is assumed correct.
test "Random float" {
var prng = DefaultPrng.init(0);
+ const random = prng.random();
var i: usize = 0;
while (i < 1000) : (i += 1) {
- const val1 = prng.random.float(f32);
+ const val1 = random.float(f32);
try expect(val1 >= 0.0);
try expect(val1 < 1.0);
- const val2 = prng.random.float(f64);
+ const val2 = random.float(f64);
try expect(val2 >= 0.0);
try expect(val2 < 1.0);
}
@@ -562,13 +594,14 @@ test "Random float" {
test "Random shuffle" {
var prng = DefaultPrng.init(0);
+ const random = prng.random();
var seq = [_]u8{ 0, 1, 2, 3, 4 };
var seen = [_]bool{false} ** 5;
var i: usize = 0;
while (i < 1000) : (i += 1) {
- prng.random.shuffle(u8, seq[0..]);
+ random.shuffle(u8, seq[0..]);
seen[seq[0]] = true;
try expect(sumArray(seq[0..]) == 10);
}
@@ -588,17 +621,19 @@ fn sumArray(s: []const u8) u32 {
test "Random range" {
var prng = DefaultPrng.init(0);
- try testRange(&prng.random, -4, 3);
- try testRange(&prng.random, -4, -1);
- try testRange(&prng.random, 10, 14);
- try testRange(&prng.random, -0x80, 0x7f);
+ const random = prng.random();
+
+ try testRange(random, -4, 3);
+ try testRange(random, -4, -1);
+ try testRange(random, 10, 14);
+ try testRange(random, -0x80, 0x7f);
}
-fn testRange(r: *Random, start: i8, end: i8) !void {
+fn testRange(r: Random, start: i8, end: i8) !void {
try testRangeBias(r, start, end, true);
try testRangeBias(r, start, end, false);
}
-fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) !void {
+fn testRangeBias(r: Random, start: i8, end: i8, biased: bool) !void {
const count = @intCast(usize, @as(i32, end) - @as(i32, start));
var values_buffer = [_]bool{false} ** 0x100;
const values = values_buffer[0..count];
@@ -617,9 +652,10 @@ test "CSPRNG" {
var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
std.crypto.random.bytes(&secret_seed);
var csprng = DefaultCsprng.init(secret_seed);
- const a = csprng.random.int(u64);
- const b = csprng.random.int(u64);
- const c = csprng.random.int(u64);
+ const random = csprng.random();
+ const a = random.int(u64);
+ const b = random.int(u64);
+ const c = random.int(u64);
try expect(a ^ b ^ c != 0);
}
lib/std/sort.zig
@@ -1328,16 +1328,17 @@ test "another sort case" {
test "sort fuzz testing" {
var prng = std.rand.DefaultPrng.init(0x12345678);
+ const random = prng.random();
const test_case_count = 10;
var i: usize = 0;
while (i < test_case_count) : (i += 1) {
- try fuzzTest(&prng.random);
+ try fuzzTest(random);
}
}
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
-fn fuzzTest(rng: *std.rand.Random) !void {
+fn fuzzTest(rng: std.rand.Random) !void {
const array_size = rng.intRangeLessThan(usize, 0, 1000);
var array = try testing.allocator.alloc(IdAndValue, array_size);
defer testing.allocator.free(array);