diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig index b93b4b7c0730..b42d918d0616 100644 --- a/lib/std/atomic/queue.zig +++ b/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, diff --git a/lib/std/atomic/stack.zig b/lib/std/atomic/stack.zig index 1005195b29f1..35f691425224 100644 --- a/lib/std/atomic/stack.zig +++ b/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, diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 414880b1cae0..4b8fbf422338 100644 --- a/lib/std/crypto/benchmark.zig +++ b/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); diff --git a/lib/std/crypto/tlcsprng.zig b/lib/std/crypto/tlcsprng.zig index 3e92c0ae181b..a51190c8bd52 100644 --- a/lib/std/crypto/tlcsprng.zig +++ b/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); diff --git a/lib/std/hash/benchmark.zig b/lib/std/hash/benchmark.zig index 9e6ec5b6bcf7..42102d644e39 100644 --- a/lib/std/hash/benchmark.zig +++ b/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(); diff --git a/lib/std/hash_map.zig b/lib/std/hash_map.zig index a75178d4286f..002a0de9010c 100644 --- a/lib/std/hash_map.zig +++ b/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]; diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index f5cdbc279244..dfff7b589623 100644 --- a/lib/std/io/test.zig +++ b/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, .{}); diff --git a/lib/std/math/big/rational.zig b/lib/std/math/big/rational.zig index 64f444eecafe..1f66417496ca 100644 --- a/lib/std/math/big/rational.zig +++ b/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); } diff --git a/lib/std/priority_dequeue.zig b/lib/std/priority_dequeue.zig index 59f3e15ff67d..e8862ae6313e 100644 --- a/lib/std/priority_dequeue.zig +++ b/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); diff --git a/lib/std/rand.zig b/lib/std/rand.zig index bafc324b91fa..7a638a76b351 100644 --- a/lib/std/rand.zig +++ b/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); } diff --git a/lib/std/rand/Gimli.zig b/lib/std/rand/Gimli.zig index 3f1488aa6243..0a95b2fd433f 100644 --- a/lib/std/rand/Gimli.zig +++ b/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 { diff --git a/lib/std/rand/Isaac64.zig b/lib/std/rand/Isaac64.zig index c3821d81aaec..787d8fc29dc0 100644 --- a/lib/std/rand/Isaac64.zig +++ b/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..])); } } diff --git a/lib/std/rand/Pcg.zig b/lib/std/rand/Pcg.zig index 5c9789aa7bdf..0129e755332c 100644 --- a/lib/std/rand/Pcg.zig +++ b/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..])); } } diff --git a/lib/std/rand/Sfc64.zig b/lib/std/rand/Sfc64.zig index 5808376e78f5..9be44b06a9cf 100644 --- a/lib/std/rand/Sfc64.zig +++ b/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..])); } } diff --git a/lib/std/rand/Xoroshiro128.zig b/lib/std/rand/Xoroshiro128.zig index 43dc95afb16a..ba103bcf20ca 100644 --- a/lib/std/rand/Xoroshiro128.zig +++ b/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..])); } } diff --git a/lib/std/rand/Xoshiro256.zig b/lib/std/rand/Xoshiro256.zig index c95cfc164de8..53cac3d5bedb 100644 --- a/lib/std/rand/Xoshiro256.zig +++ b/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..])); } } diff --git a/lib/std/rand/ziggurat.zig b/lib/std/rand/ziggurat.zig index 67cd996a3087..59b7e5339578 100644 --- a/lib/std/rand/ziggurat.zig +++ b/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); } } diff --git a/lib/std/sort.zig b/lib/std/sort.zig index 60c8f32dc616..008d58649773 100644 --- a/lib/std/sort.zig +++ b/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);