authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-18 23:57:46-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-12-18 23:57:46-05:00
log506af7e52e0985b410ea089bf5fa3247ab2377cb
tree2ec26d70f41a1382b736b606ebfa094ace62573e
parentce65533985caa9e2da567948e36d7d4ba0185005
parentf416535768fc30195cad6cd481f73fd1e80082aa
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7482 from ziglang/tlcsprng

std: introduce a thread-local CSPRNG for general use

33 files changed, 922 insertions(+), 656 deletions(-)

lib/std/c.zig+5
......@@ -264,6 +264,11 @@ pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: us
264264pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
265265pub extern "c" fn pthread_self() pthread_t;
266266pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
267pub extern "c" fn pthread_atfork(
268 prepare: ?fn () callconv(.C) void,
269 parent: ?fn () callconv(.C) void,
270 child: ?fn () callconv(.C) void,
271) c_int;
267272
268273pub extern "c" fn kqueue() c_int;
269274pub extern "c" fn kevent(
lib/std/c/linux.zig+6
......@@ -106,6 +106,12 @@ pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *con
106106pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
107107pub extern "c" fn malloc_usable_size(?*const c_void) usize;
108108
109pub extern "c" fn madvise(
110 addr: *align(std.mem.page_size) c_void,
111 length: usize,
112 advice: c_uint,
113) c_int;
114
109115pub const pthread_attr_t = extern struct {
110116 __size: [56]u8,
111117 __align: c_long,
lib/std/crypto.zig+10-1
......@@ -134,8 +134,10 @@ pub const nacl = struct {
134134
135135pub const utils = @import("crypto/utils.zig");
136136
137/// This is a thread-local, cryptographically secure pseudo random number generator.
138pub const random = &@import("crypto/tlcsprng.zig").interface;
139
137140const std = @import("std.zig");
138pub const randomBytes = std.os.getrandom;
139141
140142test "crypto" {
141143 inline for (std.meta.declarations(@This())) |decl| {
......@@ -178,6 +180,13 @@ test "crypto" {
178180 _ = @import("crypto/25519/ristretto255.zig");
179181}
180182
183test "CSPRNG" {
184 const a = random.int(u64);
185 const b = random.int(u64);
186 const c = random.int(u64);
187 std.testing.expect(a ^ b ^ c != 0);
188}
189
181190test "issue #4532: no index out of bounds" {
182191 const types = [_]type{
183192 hash.Md5,
lib/std/crypto/25519/ed25519.zig+4-4
......@@ -43,7 +43,7 @@ pub const Ed25519 = struct {
4343 pub fn create(seed: ?[seed_length]u8) !KeyPair {
4444 const ss = seed orelse ss: {
4545 var random_seed: [seed_length]u8 = undefined;
46 try crypto.randomBytes(&random_seed);
46 crypto.random.bytes(&random_seed);
4747 break :ss random_seed;
4848 };
4949 var az: [Sha512.digest_length]u8 = undefined;
......@@ -179,7 +179,7 @@ pub const Ed25519 = struct {
179179
180180 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
181181 for (z_batch) |*z| {
182 try std.crypto.randomBytes(z[0..16]);
182 std.crypto.random.bytes(z[0..16]);
183183 mem.set(u8, z[16..], 0);
184184 }
185185
......@@ -232,8 +232,8 @@ test "ed25519 batch verification" {
232232 const key_pair = try Ed25519.KeyPair.create(null);
233233 var msg1: [32]u8 = undefined;
234234 var msg2: [32]u8 = undefined;
235 try std.crypto.randomBytes(&msg1);
236 try std.crypto.randomBytes(&msg2);
235 std.crypto.random.bytes(&msg1);
236 std.crypto.random.bytes(&msg2);
237237 const sig1 = try Ed25519.sign(&msg1, key_pair, null);
238238 const sig2 = try Ed25519.sign(&msg2, key_pair, null);
239239 var signature_batch = [_]Ed25519.BatchElement{
lib/std/crypto/25519/edwards25519.zig+2-2
......@@ -484,8 +484,8 @@ test "edwards25519 packing/unpacking" {
484484test "edwards25519 point addition/substraction" {
485485 var s1: [32]u8 = undefined;
486486 var s2: [32]u8 = undefined;
487 try std.crypto.randomBytes(&s1);
488 try std.crypto.randomBytes(&s2);
487 std.crypto.random.bytes(&s1);
488 std.crypto.random.bytes(&s2);
489489 const p = try Edwards25519.basePoint.clampedMul(s1);
490490 const q = try Edwards25519.basePoint.clampedMul(s2);
491491 const r = p.add(q).add(q).sub(q).sub(q);
lib/std/crypto/25519/x25519.zig+1-1
......@@ -34,7 +34,7 @@ pub const X25519 = struct {
3434 pub fn create(seed: ?[seed_length]u8) !KeyPair {
3535 const sk = seed orelse sk: {
3636 var random_seed: [seed_length]u8 = undefined;
37 try crypto.randomBytes(&random_seed);
37 crypto.random.bytes(&random_seed);
3838 break :sk random_seed;
3939 };
4040 var kp: KeyPair = undefined;
lib/std/crypto/bcrypt.zig+2-2
......@@ -262,7 +262,7 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
262262/// and then use the resulting hash as the password parameter for bcrypt.
263263pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {
264264 var salt: [salt_length]u8 = undefined;
265 try crypto.randomBytes(&salt);
265 crypto.random.bytes(&salt);
266266 return strHashInternal(password, rounds_log, salt);
267267}
268268
......@@ -283,7 +283,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {
283283
284284test "bcrypt codec" {
285285 var salt: [salt_length]u8 = undefined;
286 try crypto.randomBytes(&salt);
286 crypto.random.bytes(&salt);
287287 var salt_str: [salt_str_length]u8 = undefined;
288288 Codec.encode(salt_str[0..], salt[0..]);
289289 var salt2: [salt_length]u8 = undefined;
lib/std/crypto/salsa20.zig+9-9
......@@ -571,9 +571,9 @@ test "xsalsa20poly1305" {
571571 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
572572 var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;
573573 var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;
574 try crypto.randomBytes(&msg);
575 try crypto.randomBytes(&key);
576 try crypto.randomBytes(&nonce);
574 crypto.random.bytes(&msg);
575 crypto.random.bytes(&key);
576 crypto.random.bytes(&nonce);
577577
578578 XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);
579579 try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);
......@@ -585,9 +585,9 @@ test "xsalsa20poly1305 secretbox" {
585585 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
586586 var nonce: [Box.nonce_length]u8 = undefined;
587587 var boxed: [msg.len + Box.tag_length]u8 = undefined;
588 try crypto.randomBytes(&msg);
589 try crypto.randomBytes(&key);
590 try crypto.randomBytes(&nonce);
588 crypto.random.bytes(&msg);
589 crypto.random.bytes(&key);
590 crypto.random.bytes(&nonce);
591591
592592 SecretBox.seal(boxed[0..], msg[0..], nonce, key);
593593 try SecretBox.open(msg2[0..], boxed[0..], nonce, key);
......@@ -598,8 +598,8 @@ test "xsalsa20poly1305 box" {
598598 var msg2: [msg.len]u8 = undefined;
599599 var nonce: [Box.nonce_length]u8 = undefined;
600600 var boxed: [msg.len + Box.tag_length]u8 = undefined;
601 try crypto.randomBytes(&msg);
602 try crypto.randomBytes(&nonce);
601 crypto.random.bytes(&msg);
602 crypto.random.bytes(&nonce);
603603
604604 var kp1 = try Box.KeyPair.create(null);
605605 var kp2 = try Box.KeyPair.create(null);
......@@ -611,7 +611,7 @@ test "xsalsa20poly1305 sealedbox" {
611611 var msg: [100]u8 = undefined;
612612 var msg2: [msg.len]u8 = undefined;
613613 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
614 try crypto.randomBytes(&msg);
614 crypto.random.bytes(&msg);
615615
616616 var kp = try Box.KeyPair.create(null);
617617 try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);
lib/std/crypto/tlcsprng.zig created+158
......@@ -0,0 +1,158 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! Thread-local cryptographically secure pseudo-random number generator.
8//! This file has public declarations that are intended to be used internally
9//! by the standard library; this namespace is not intended to be exposed
10//! directly to standard library users.
11
12const std = @import("std");
13const root = @import("root");
14const mem = std.mem;
15
16/// We use this as a layer of indirection because global const pointers cannot
17/// point to thread-local variables.
18pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill };
19
20const os_has_fork = switch (std.Target.current.os.tag) {
21 .dragonfly,
22 .freebsd,
23 .ios,
24 .kfreebsd,
25 .linux,
26 .macos,
27 .netbsd,
28 .openbsd,
29 .solaris,
30 .tvos,
31 .watchos,
32 => true,
33
34 else => false,
35};
36const os_has_arc4random = std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf");
37const want_fork_safety = os_has_fork and !os_has_arc4random and
38 (std.meta.globalOption("crypto_fork_safety", bool) orelse true);
39const maybe_have_wipe_on_fork = std.Target.current.os.isAtLeast(.linux, .{
40 .major = 4,
41 .minor = 14,
42}) orelse true;
43
44const WipeMe = struct {
45 init_state: enum { uninitialized, initialized, failed },
46 gimli: std.crypto.core.Gimli,
47};
48const wipe_align = if (maybe_have_wipe_on_fork) mem.page_size else @alignOf(WipeMe);
49
50threadlocal var wipe_me: WipeMe align(wipe_align) = .{
51 .gimli = undefined,
52 .init_state = .uninitialized,
53};
54
55fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void {
56 if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) {
57 // arc4random is already a thread-local CSPRNG.
58 return std.c.arc4random_buf(buffer.ptr, buffer.len);
59 }
60 // Allow applications to decide they would prefer to have every call to
61 // std.crypto.random always make an OS syscall, rather than rely on an
62 // application implementation of a CSPRNG.
63 if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) {
64 return fillWithOsEntropy(buffer);
65 }
66 switch (wipe_me.init_state) {
67 .uninitialized => {
68 if (want_fork_safety) {
69 if (maybe_have_wipe_on_fork) {
70 if (std.os.madvise(
71 @ptrCast([*]align(mem.page_size) u8, &wipe_me),
72 @sizeOf(@TypeOf(wipe_me)),
73 std.os.MADV_WIPEONFORK,
74 )) |_| {
75 return initAndFill(buffer);
76 } else |_| if (std.Thread.use_pthreads) {
77 return setupPthreadAtforkAndFill(buffer);
78 } else {
79 // Since we failed to set up fork safety, we fall back to always
80 // calling getrandom every time.
81 wipe_me.init_state = .failed;
82 return fillWithOsEntropy(buffer);
83 }
84 } else if (std.Thread.use_pthreads) {
85 return setupPthreadAtforkAndFill(buffer);
86 } else {
87 // We have no mechanism to provide fork safety, but we want fork safety,
88 // so we fall back to calling getrandom every time.
89 wipe_me.init_state = .failed;
90 return fillWithOsEntropy(buffer);
91 }
92 } else {
93 return initAndFill(buffer);
94 }
95 },
96 .initialized => {
97 return fillWithCsprng(buffer);
98 },
99 .failed => {
100 if (want_fork_safety) {
101 return fillWithOsEntropy(buffer);
102 } else {
103 unreachable;
104 }
105 },
106 }
107}
108
109fn setupPthreadAtforkAndFill(buffer: []u8) void {
110 const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0;
111 if (failed) {
112 wipe_me.init_state = .failed;
113 return fillWithOsEntropy(buffer);
114 } else {
115 return initAndFill(buffer);
116 }
117}
118
119fn childAtForkHandler() callconv(.C) void {
120 // TODO this is a workaround for https://github.com/ziglang/zig/issues/7495
121 var wipe_slice: []u8 = undefined;
122 wipe_slice = @ptrCast([*]u8, &wipe_me)[0..@sizeOf(@TypeOf(wipe_me))];
123 std.crypto.utils.secureZero(u8, wipe_slice);
124}
125
126fn fillWithCsprng(buffer: []u8) void {
127 if (buffer.len != 0) {
128 wipe_me.gimli.squeeze(buffer);
129 } else {
130 wipe_me.gimli.permute();
131 }
132 mem.set(u8, wipe_me.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
133}
134
135fn fillWithOsEntropy(buffer: []u8) void {
136 std.os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
137}
138
139fn initAndFill(buffer: []u8) void {
140 var seed: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
141 // Because we panic on getrandom() failing, we provide the opportunity
142 // to override the default seed function. This also makes
143 // `std.crypto.random` available on freestanding targets, provided that
144 // the `cryptoRandomSeed` function is provided.
145 if (@hasDecl(root, "cryptoRandomSeed")) {
146 root.cryptoRandomSeed(&seed);
147 } else {
148 fillWithOsEntropy(&seed);
149 }
150
151 wipe_me.gimli = std.crypto.core.Gimli.init(seed);
152
153 // This is at the end so that accidental recursive dependencies result
154 // in stack overflows instead of invalid random data.
155 wipe_me.init_state = .initialized;
156
157 return fillWithCsprng(buffer);
158}
lib/std/crypto/utils.zig+4-4
......@@ -51,8 +51,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
5151test "crypto.utils.timingSafeEql" {
5252 var a: [100]u8 = undefined;
5353 var b: [100]u8 = undefined;
54 try std.crypto.randomBytes(a[0..]);
55 try std.crypto.randomBytes(b[0..]);
54 std.crypto.random.bytes(a[0..]);
55 std.crypto.random.bytes(b[0..]);
5656 testing.expect(!timingSafeEql([100]u8, a, b));
5757 mem.copy(u8, a[0..], b[0..]);
5858 testing.expect(timingSafeEql([100]u8, a, b));
......@@ -61,8 +61,8 @@ test "crypto.utils.timingSafeEql" {
6161test "crypto.utils.timingSafeEql (vectors)" {
6262 var a: [100]u8 = undefined;
6363 var b: [100]u8 = undefined;
64 try std.crypto.randomBytes(a[0..]);
65 try std.crypto.randomBytes(b[0..]);
64 std.crypto.random.bytes(a[0..]);
65 std.crypto.random.bytes(b[0..]);
6666 const v1: std.meta.Vector(100, u8) = a;
6767 const v2: std.meta.Vector(100, u8) = b;
6868 testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2));
lib/std/fs.zig+2-2
......@@ -82,7 +82,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
8282 mem.copy(u8, tmp_path[0..], dirname);
8383 tmp_path[dirname.len] = path.sep;
8484 while (true) {
85 try crypto.randomBytes(rand_buf[0..]);
85 crypto.random.bytes(rand_buf[0..]);
8686 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
8787
8888 if (cwd().symLink(existing_path, tmp_path, .{})) {
......@@ -157,7 +157,7 @@ pub const AtomicFile = struct {
157157 tmp_path_buf[base64.Base64Encoder.calcSize(RANDOM_BYTES)] = 0;
158158
159159 while (true) {
160 try crypto.randomBytes(rand_buf[0..]);
160 crypto.random.bytes(rand_buf[0..]);
161161 base64_encoder.encode(&tmp_path_buf, &rand_buf);
162162
163163 const file = dir.createFile(
lib/std/meta.zig+8
......@@ -9,6 +9,7 @@ const debug = std.debug;
99const mem = std.mem;
1010const math = std.math;
1111const testing = std.testing;
12const root = @import("root");
1213
1314pub const trait = @import("meta/trait.zig");
1415pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
......@@ -1085,3 +1086,10 @@ test "Tuple" {
10851086 TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
10861087 TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void }));
10871088}
1089
1090/// TODO: https://github.com/ziglang/zig/issues/425
1091pub fn globalOption(comptime name: []const u8, comptime T: type) ?T {
1092 if (!@hasDecl(root, name))
1093 return null;
1094 return @as(T, @field(root, name));
1095}
lib/std/os.zig+48
......@@ -5845,3 +5845,51 @@ pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void
58455845 else => |err| return unexpectedErrno(err),
58465846 }
58475847}
5848
5849pub const MadviseError = error{
5850 /// advice is MADV_REMOVE, but the specified address range is not a shared writable mapping.
5851 AccessDenied,
5852 /// advice is MADV_HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
5853 PermissionDenied,
5854 /// A kernel resource was temporarily unavailable.
5855 SystemResources,
5856 /// One of the following:
5857 /// * addr is not page-aligned or length is negative
5858 /// * advice is not valid
5859 /// * advice is MADV_DONTNEED or MADV_REMOVE and the specified address range
5860 /// includes locked, Huge TLB pages, or VM_PFNMAP pages.
5861 /// * advice is MADV_MERGEABLE or MADV_UNMERGEABLE, but the kernel was not
5862 /// configured with CONFIG_KSM.
5863 /// * advice is MADV_FREE or MADV_WIPEONFORK but the specified address range
5864 /// includes file, Huge TLB, MAP_SHARED, or VM_PFNMAP ranges.
5865 InvalidSyscall,
5866 /// (for MADV_WILLNEED) Paging in this area would exceed the process's
5867 /// maximum resident set size.
5868 WouldExceedMaximumResidentSetSize,
5869 /// One of the following:
5870 /// * (for MADV_WILLNEED) Not enough memory: paging in failed.
5871 /// * Addresses in the specified range are not currently mapped, or
5872 /// are outside the address space of the process.
5873 OutOfMemory,
5874 /// The madvise syscall is not available on this version and configuration
5875 /// of the Linux kernel.
5876 MadviseUnavailable,
5877 /// The operating system returned an undocumented error code.
5878 Unexpected,
5879};
5880
5881/// Give advice about use of memory.
5882/// This syscall is optional and is sometimes configured to be disabled.
5883pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
5884 switch (errno(system.madvise(ptr, length, advice))) {
5885 0 => return,
5886 EACCES => return error.AccessDenied,
5887 EAGAIN => return error.SystemResources,
5888 EBADF => unreachable, // The map exists, but the area maps something that isn't a file.
5889 EINVAL => return error.InvalidSyscall,
5890 EIO => return error.WouldExceedMaximumResidentSetSize,
5891 ENOMEM => return error.OutOfMemory,
5892 ENOSYS => return error.MadviseUnavailable,
5893 else => |err| return unexpectedErrno(err),
5894 }
5895}
lib/std/os/bits/linux.zig+22
......@@ -2045,3 +2045,25 @@ pub const rlimit = extern struct {
20452045 /// Hard limit
20462046 max: rlim_t,
20472047};
2048
2049pub const MADV_NORMAL = 0;
2050pub const MADV_RANDOM = 1;
2051pub const MADV_SEQUENTIAL = 2;
2052pub const MADV_WILLNEED = 3;
2053pub const MADV_DONTNEED = 4;
2054pub const MADV_FREE = 8;
2055pub const MADV_REMOVE = 9;
2056pub const MADV_DONTFORK = 10;
2057pub const MADV_DOFORK = 11;
2058pub const MADV_MERGEABLE = 12;
2059pub const MADV_UNMERGEABLE = 13;
2060pub const MADV_HUGEPAGE = 14;
2061pub const MADV_NOHUGEPAGE = 15;
2062pub const MADV_DONTDUMP = 16;
2063pub const MADV_DODUMP = 17;
2064pub const MADV_WIPEONFORK = 18;
2065pub const MADV_KEEPONFORK = 19;
2066pub const MADV_COLD = 20;
2067pub const MADV_PAGEOUT = 21;
2068pub const MADV_HWPOISON = 100;
2069pub const MADV_SOFT_OFFLINE = 101;
lib/std/os/linux.zig+4
......@@ -1351,6 +1351,10 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit,
13511351 );
13521352}
13531353
1354pub fn madvise(address: [*]u8, len: usize, advice: u32) usize {
1355 return syscall3(.madvise, @ptrToInt(address), len, advice);
1356}
1357
13541358test "" {
13551359 if (builtin.os.tag == .linux) {
13561360 _ = @import("linux/test.zig");
lib/std/os/linux/tls.zig+24-15
......@@ -327,34 +327,43 @@ pub fn prepareTLS(area: []u8) usize {
327327 if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset;
328328}
329329
330var main_thread_tls_buffer: [256]u8 = undefined;
330// The main motivation for the size chosen here is this is how much ends up being
331// requested for the thread local variables of the std.crypto.random implementation.
332// I'm not sure why it ends up being so much; the struct itself is only 64 bytes.
333// I think it has to do with being page aligned and LLVM or LLD is not smart enough
334// to lay out the TLS data in a space conserving way. Anyway I think it's fine
335// because it's less than 3 pages of memory, and putting it in the ELF like this
336// is equivalent to moving the mmap call below into the kernel, avoiding syscall
337// overhead.
338var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
331339
332340pub fn initStaticTLS() void {
333341 initTLS();
334342
335 const alloc_tls_area: []u8 = blk: {
336 const full_alloc_size = tls_image.alloc_size + tls_image.alloc_align - 1;
337
343 const tls_area = blk: {
338344 // Fast path for the common case where the TLS data is really small,
339 // avoid an allocation and use our local buffer
340 if (full_alloc_size < main_thread_tls_buffer.len)
341 break :blk main_thread_tls_buffer[0..];
345 // avoid an allocation and use our local buffer.
346 if (tls_image.alloc_align <= mem.page_size and
347 tls_image.alloc_size <= main_thread_tls_buffer.len)
348 {
349 break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
350 }
342351
343 break :blk os.mmap(
352 const alloc_tls_area = os.mmap(
344353 null,
345 full_alloc_size,
354 tls_image.alloc_size + tls_image.alloc_align - 1,
346355 os.PROT_READ | os.PROT_WRITE,
347356 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
348357 -1,
349358 0,
350359 ) catch os.abort();
351 };
352360
353 // Make sure the slice is correctly aligned
354 const begin_addr = @ptrToInt(alloc_tls_area.ptr);
355 const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align);
356 const start = begin_aligned_addr - begin_addr;
357 const tls_area = alloc_tls_area[start .. start + tls_image.alloc_size];
361 // Make sure the slice is correctly aligned.
362 const begin_addr = @ptrToInt(alloc_tls_area.ptr);
363 const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align);
364 const start = begin_aligned_addr - begin_addr;
365 break :blk alloc_tls_area[start .. start + tls_image.alloc_size];
366 };
358367
359368 const tp_value = prepareTLS(tls_area);
360369 setThreadPointer(tp_value);
lib/std/rand.zig+13-566
......@@ -4,19 +4,11 @@
44// The MIT license requires this copyright notice to be included in all copies
55// and substantial portions of the software.
66
7//! The engines provided here should be initialized from an external source. For now, randomBytes
8//! from the crypto package is the most suitable. Be sure to use a CSPRNG when required, otherwise using
9//! a normal PRNG will be faster and use substantially less stack space.
10//!
11//! ```
12//! var buf: [8]u8 = undefined;
13//! try std.crypto.randomBytes(buf[0..]);
14//! const seed = mem.readIntLittle(u64, buf[0..8]);
15//!
16//! var r = DefaultPrng.init(seed);
17//!
18//! const s = r.random.int(u64);
19//! ```
7//! The engines provided here should be initialized from an external source.
8//! For a thread-local cryptographically secure pseudo random number generator,
9//! use `std.crypto.random`.
10//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
11//! be faster and use substantially less stack space.
2012//!
2113//! TODO(tiehuis): Benchmark these against other reference implementations.
2214
......@@ -36,6 +28,12 @@ pub const DefaultPrng = Xoroshiro128;
3628/// Cryptographically secure random numbers.
3729pub const DefaultCsprng = Gimli;
3830
31pub const Isaac64 = @import("rand/Isaac64.zig");
32pub const Gimli = @import("rand/Gimli.zig");
33pub const Pcg = @import("rand/Pcg.zig");
34pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig");
35pub const Sfc64 = @import("rand/Sfc64.zig");
36
3937pub const Random = struct {
4038 fillFn: fn (r: *Random, buf: []u8) void,
4139
......@@ -491,7 +489,7 @@ test "Random Biased" {
491489//
492490// The number of cycles is thus limited to 64-bits regardless of the engine, but this
493491// is still plenty for practical purposes.
494const SplitMix64 = struct {
492pub const SplitMix64 = struct {
495493 s: u64,
496494
497495 pub fn init(seed: u64) SplitMix64 {
......@@ -525,557 +523,6 @@ test "splitmix64 sequence" {
525523 }
526524}
527525
528// PCG32 - http://www.pcg-random.org/
529//
530// PRNG
531pub const Pcg = struct {
532 const default_multiplier = 6364136223846793005;
533
534 random: Random,
535
536 s: u64,
537 i: u64,
538
539 pub fn init(init_s: u64) Pcg {
540 var pcg = Pcg{
541 .random = Random{ .fillFn = fill },
542 .s = undefined,
543 .i = undefined,
544 };
545
546 pcg.seed(init_s);
547 return pcg;
548 }
549
550 fn next(self: *Pcg) u32 {
551 const l = self.s;
552 self.s = l *% default_multiplier +% (self.i | 1);
553
554 const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
555 const rot = @intCast(u32, l >> 59);
556
557 return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31));
558 }
559
560 fn seed(self: *Pcg, init_s: u64) void {
561 // Pcg requires 128-bits of seed.
562 var gen = SplitMix64.init(init_s);
563 self.seedTwo(gen.next(), gen.next());
564 }
565
566 fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void {
567 self.s = 0;
568 self.i = (init_s << 1) | 1;
569 self.s = self.s *% default_multiplier +% self.i;
570 self.s +%= init_i;
571 self.s = self.s *% default_multiplier +% self.i;
572 }
573
574 fn fill(r: *Random, buf: []u8) void {
575 const self = @fieldParentPtr(Pcg, "random", r);
576
577 var i: usize = 0;
578 const aligned_len = buf.len - (buf.len & 7);
579
580 // Complete 4 byte segments.
581 while (i < aligned_len) : (i += 4) {
582 var n = self.next();
583 comptime var j: usize = 0;
584 inline while (j < 4) : (j += 1) {
585 buf[i + j] = @truncate(u8, n);
586 n >>= 8;
587 }
588 }
589
590 // Remaining. (cuts the stream)
591 if (i != buf.len) {
592 var n = self.next();
593 while (i < buf.len) : (i += 1) {
594 buf[i] = @truncate(u8, n);
595 n >>= 4;
596 }
597 }
598 }
599};
600
601test "pcg sequence" {
602 var r = Pcg.init(0);
603 const s0: u64 = 0x9394bf54ce5d79de;
604 const s1: u64 = 0x84e9c579ef59bbf7;
605 r.seedTwo(s0, s1);
606
607 const seq = [_]u32{
608 2881561918,
609 3063928540,
610 1199791034,
611 2487695858,
612 1479648952,
613 3247963454,
614 };
615
616 for (seq) |s| {
617 expect(s == r.next());
618 }
619}
620
621// Xoroshiro128+ - http://xoroshiro.di.unimi.it/
622//
623// PRNG
624pub const Xoroshiro128 = struct {
625 random: Random,
626
627 s: [2]u64,
628
629 pub fn init(init_s: u64) Xoroshiro128 {
630 var x = Xoroshiro128{
631 .random = Random{ .fillFn = fill },
632 .s = undefined,
633 };
634
635 x.seed(init_s);
636 return x;
637 }
638
639 fn next(self: *Xoroshiro128) u64 {
640 const s0 = self.s[0];
641 var s1 = self.s[1];
642 const r = s0 +% s1;
643
644 s1 ^= s0;
645 self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
646 self.s[1] = math.rotl(u64, s1, @as(u8, 36));
647
648 return r;
649 }
650
651 // Skip 2^64 places ahead in the sequence
652 fn jump(self: *Xoroshiro128) void {
653 var s0: u64 = 0;
654 var s1: u64 = 0;
655
656 const table = [_]u64{
657 0xbeac0467eba5facb,
658 0xd86b048b86aa9922,
659 };
660
661 inline for (table) |entry| {
662 var b: usize = 0;
663 while (b < 64) : (b += 1) {
664 if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) {
665 s0 ^= self.s[0];
666 s1 ^= self.s[1];
667 }
668 _ = self.next();
669 }
670 }
671
672 self.s[0] = s0;
673 self.s[1] = s1;
674 }
675
676 pub fn seed(self: *Xoroshiro128, init_s: u64) void {
677 // Xoroshiro requires 128-bits of seed.
678 var gen = SplitMix64.init(init_s);
679
680 self.s[0] = gen.next();
681 self.s[1] = gen.next();
682 }
683
684 fn fill(r: *Random, buf: []u8) void {
685 const self = @fieldParentPtr(Xoroshiro128, "random", r);
686
687 var i: usize = 0;
688 const aligned_len = buf.len - (buf.len & 7);
689
690 // Complete 8 byte segments.
691 while (i < aligned_len) : (i += 8) {
692 var n = self.next();
693 comptime var j: usize = 0;
694 inline while (j < 8) : (j += 1) {
695 buf[i + j] = @truncate(u8, n);
696 n >>= 8;
697 }
698 }
699
700 // Remaining. (cuts the stream)
701 if (i != buf.len) {
702 var n = self.next();
703 while (i < buf.len) : (i += 1) {
704 buf[i] = @truncate(u8, n);
705 n >>= 8;
706 }
707 }
708 }
709};
710
711test "xoroshiro sequence" {
712 var r = Xoroshiro128.init(0);
713 r.s[0] = 0xaeecf86f7878dd75;
714 r.s[1] = 0x01cd153642e72622;
715
716 const seq1 = [_]u64{
717 0xb0ba0da5bb600397,
718 0x18a08afde614dccc,
719 0xa2635b956a31b929,
720 0xabe633c971efa045,
721 0x9ac19f9706ca3cac,
722 0xf62b426578c1e3fb,
723 };
724
725 for (seq1) |s| {
726 expect(s == r.next());
727 }
728
729 r.jump();
730
731 const seq2 = [_]u64{
732 0x95344a13556d3e22,
733 0xb4fb32dafa4d00df,
734 0xb2011d9ccdcfe2dd,
735 0x05679a9b2119b908,
736 0xa860a1da7c9cd8a0,
737 0x658a96efe3f86550,
738 };
739
740 for (seq2) |s| {
741 expect(s == r.next());
742 }
743}
744
745// Gimli
746//
747// CSPRNG
748pub const Gimli = struct {
749 random: Random,
750 state: std.crypto.core.Gimli,
751
752 pub const secret_seed_length = 32;
753
754 /// The seed must be uniform, secret and `secret_seed_length` bytes long.
755 /// It can be generated using `std.crypto.randomBytes()`.
756 pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
757 var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
758 mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
759 mem.set(u8, initial_state[secret_seed_length..], 0);
760 var self = Gimli{
761 .random = Random{ .fillFn = fill },
762 .state = std.crypto.core.Gimli.init(initial_state),
763 };
764 return self;
765 }
766
767 fn fill(r: *Random, buf: []u8) void {
768 const self = @fieldParentPtr(Gimli, "random", r);
769
770 if (buf.len != 0) {
771 self.state.squeeze(buf);
772 } else {
773 self.state.permute();
774 }
775 mem.set(u8, self.state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
776 }
777};
778
779// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
780//
781// Follows the general idea of the implementation from here with a few shortcuts.
782// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
783pub const Isaac64 = struct {
784 random: Random,
785
786 r: [256]u64,
787 m: [256]u64,
788 a: u64,
789 b: u64,
790 c: u64,
791 i: usize,
792
793 pub fn init(init_s: u64) Isaac64 {
794 var isaac = Isaac64{
795 .random = Random{ .fillFn = fill },
796 .r = undefined,
797 .m = undefined,
798 .a = undefined,
799 .b = undefined,
800 .c = undefined,
801 .i = undefined,
802 };
803
804 // seed == 0 => same result as the unseeded reference implementation
805 isaac.seed(init_s, 1);
806 return isaac;
807 }
808
809 fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
810 const x = self.m[base + m1];
811 self.a = mix +% self.m[base + m2];
812
813 const y = self.a +% self.b +% self.m[@intCast(usize, (x >> 3) % self.m.len)];
814 self.m[base + m1] = y;
815
816 self.b = x +% self.m[@intCast(usize, (y >> 11) % self.m.len)];
817 self.r[self.r.len - 1 - base - m1] = self.b;
818 }
819
820 fn refill(self: *Isaac64) void {
821 const midpoint = self.r.len / 2;
822
823 self.c +%= 1;
824 self.b +%= self.c;
825
826 {
827 var i: usize = 0;
828 while (i < midpoint) : (i += 4) {
829 self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
830 self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint);
831 self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint);
832 self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint);
833 }
834 }
835
836 {
837 var i: usize = 0;
838 while (i < midpoint) : (i += 4) {
839 self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
840 self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0);
841 self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0);
842 self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0);
843 }
844 }
845
846 self.i = 0;
847 }
848
849 fn next(self: *Isaac64) u64 {
850 if (self.i >= self.r.len) {
851 self.refill();
852 }
853
854 const value = self.r[self.i];
855 self.i += 1;
856 return value;
857 }
858
859 fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
860 // We ignore the multi-pass requirement since we don't currently expose full access to
861 // seeding the self.m array completely.
862 mem.set(u64, self.m[0..], 0);
863 self.m[0] = init_s;
864
865 // prescrambled golden ratio constants
866 var a = [_]u64{
867 0x647c4677a2884b7c,
868 0xb9f8b322c73ac862,
869 0x8c0ea5053d4712a0,
870 0xb29b2e824a595524,
871 0x82f053db8355e0ce,
872 0x48fe4a0fa5a09315,
873 0xae985bf2cbfc89ed,
874 0x98f5704f6c44c0ab,
875 };
876
877 comptime var i: usize = 0;
878 inline while (i < rounds) : (i += 1) {
879 var j: usize = 0;
880 while (j < self.m.len) : (j += 8) {
881 comptime var x1: usize = 0;
882 inline while (x1 < 8) : (x1 += 1) {
883 a[x1] +%= self.m[j + x1];
884 }
885
886 a[0] -%= a[4];
887 a[5] ^= a[7] >> 9;
888 a[7] +%= a[0];
889 a[1] -%= a[5];
890 a[6] ^= a[0] << 9;
891 a[0] +%= a[1];
892 a[2] -%= a[6];
893 a[7] ^= a[1] >> 23;
894 a[1] +%= a[2];
895 a[3] -%= a[7];
896 a[0] ^= a[2] << 15;
897 a[2] +%= a[3];
898 a[4] -%= a[0];
899 a[1] ^= a[3] >> 14;
900 a[3] +%= a[4];
901 a[5] -%= a[1];
902 a[2] ^= a[4] << 20;
903 a[4] +%= a[5];
904 a[6] -%= a[2];
905 a[3] ^= a[5] >> 17;
906 a[5] +%= a[6];
907 a[7] -%= a[3];
908 a[4] ^= a[6] << 14;
909 a[6] +%= a[7];
910
911 comptime var x2: usize = 0;
912 inline while (x2 < 8) : (x2 += 1) {
913 self.m[j + x2] = a[x2];
914 }
915 }
916 }
917
918 mem.set(u64, self.r[0..], 0);
919 self.a = 0;
920 self.b = 0;
921 self.c = 0;
922 self.i = self.r.len; // trigger refill on first value
923 }
924
925 fn fill(r: *Random, buf: []u8) void {
926 const self = @fieldParentPtr(Isaac64, "random", r);
927
928 var i: usize = 0;
929 const aligned_len = buf.len - (buf.len & 7);
930
931 // Fill complete 64-byte segments
932 while (i < aligned_len) : (i += 8) {
933 var n = self.next();
934 comptime var j: usize = 0;
935 inline while (j < 8) : (j += 1) {
936 buf[i + j] = @truncate(u8, n);
937 n >>= 8;
938 }
939 }
940
941 // Fill trailing, ignoring excess (cut the stream).
942 if (i != buf.len) {
943 var n = self.next();
944 while (i < buf.len) : (i += 1) {
945 buf[i] = @truncate(u8, n);
946 n >>= 8;
947 }
948 }
949 }
950};
951
952test "isaac64 sequence" {
953 var r = Isaac64.init(0);
954
955 // from reference implementation
956 const seq = [_]u64{
957 0xf67dfba498e4937c,
958 0x84a5066a9204f380,
959 0xfee34bd5f5514dbb,
960 0x4d1664739b8f80d6,
961 0x8607459ab52a14aa,
962 0x0e78bc5a98529e49,
963 0xfe5332822ad13777,
964 0x556c27525e33d01a,
965 0x08643ca615f3149f,
966 0xd0771faf3cb04714,
967 0x30e86f68a37b008d,
968 0x3074ebc0488a3adf,
969 0x270645ea7a2790bc,
970 0x5601a0a8d3763c6a,
971 0x2f83071f53f325dd,
972 0xb9090f3d42d2d2ea,
973 };
974
975 for (seq) |s| {
976 expect(s == r.next());
977 }
978}
979
980/// Sfc64 pseudo-random number generator from Practically Random.
981/// Fastest engine of pracrand and smallest footprint.
982/// See http://pracrand.sourceforge.net/
983pub const Sfc64 = struct {
984 random: Random,
985
986 a: u64 = undefined,
987 b: u64 = undefined,
988 c: u64 = undefined,
989 counter: u64 = undefined,
990
991 const Rotation = 24;
992 const RightShift = 11;
993 const LeftShift = 3;
994
995 pub fn init(init_s: u64) Sfc64 {
996 var x = Sfc64{
997 .random = Random{ .fillFn = fill },
998 };
999
1000 x.seed(init_s);
1001 return x;
1002 }
1003
1004 fn next(self: *Sfc64) u64 {
1005 const tmp = self.a +% self.b +% self.counter;
1006 self.counter += 1;
1007 self.a = self.b ^ (self.b >> RightShift);
1008 self.b = self.c +% (self.c << LeftShift);
1009 self.c = math.rotl(u64, self.c, Rotation) +% tmp;
1010 return tmp;
1011 }
1012
1013 fn seed(self: *Sfc64, init_s: u64) void {
1014 self.a = init_s;
1015 self.b = init_s;
1016 self.c = init_s;
1017 self.counter = 1;
1018 var i: u32 = 0;
1019 while (i < 12) : (i += 1) {
1020 _ = self.next();
1021 }
1022 }
1023
1024 fn fill(r: *Random, buf: []u8) void {
1025 const self = @fieldParentPtr(Sfc64, "random", r);
1026
1027 var i: usize = 0;
1028 const aligned_len = buf.len - (buf.len & 7);
1029
1030 // Complete 8 byte segments.
1031 while (i < aligned_len) : (i += 8) {
1032 var n = self.next();
1033 comptime var j: usize = 0;
1034 inline while (j < 8) : (j += 1) {
1035 buf[i + j] = @truncate(u8, n);
1036 n >>= 8;
1037 }
1038 }
1039
1040 // Remaining. (cuts the stream)
1041 if (i != buf.len) {
1042 var n = self.next();
1043 while (i < buf.len) : (i += 1) {
1044 buf[i] = @truncate(u8, n);
1045 n >>= 8;
1046 }
1047 }
1048 }
1049};
1050
1051test "Sfc64 sequence" {
1052 // Unfortunately there does not seem to be an official test sequence.
1053 var r = Sfc64.init(0);
1054
1055 const seq = [_]u64{
1056 0x3acfa029e3cc6041,
1057 0xf5b6515bf2ee419c,
1058 0x1259635894a29b61,
1059 0xb6ae75395f8ebd6,
1060 0x225622285ce302e2,
1061 0x520d28611395cb21,
1062 0xdb909c818901599d,
1063 0x8ffd195365216f57,
1064 0xe8c4ad5e258ac04a,
1065 0x8f8ef2c89fdb63ca,
1066 0xf9865b01d98d8e2f,
1067 0x46555871a65d08ba,
1068 0x66868677c6298fcd,
1069 0x2ce15a7e6329f57d,
1070 0xb2f1833ca91ca79,
1071 0x4b0890ac9bf453ca,
1072 };
1073
1074 for (seq) |s| {
1075 expectEqual(s, r.next());
1076 }
1077}
1078
1079526// Actual Random helper function tests, pcg engine is assumed correct.
1080527test "Random float" {
1081528 var prng = DefaultPrng.init(0);
......@@ -1147,7 +594,7 @@ fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void {
1147594
1148595test "CSPRNG" {
1149596 var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
1150 try std.crypto.randomBytes(&secret_seed);
597 std.crypto.random.bytes(&secret_seed);
1151598 var csprng = DefaultCsprng.init(secret_seed);
1152599 const a = csprng.random.int(u64);
1153600 const b = csprng.random.int(u64);
lib/std/rand/Gimli.zig created+40
......@@ -0,0 +1,40 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! CSPRNG
8
9const std = @import("std");
10const Random = std.rand.Random;
11const mem = std.mem;
12const Gimli = @This();
13
14random: Random,
15state: std.crypto.core.Gimli,
16
17pub const secret_seed_length = 32;
18
19/// The seed must be uniform, secret and `secret_seed_length` bytes long.
20pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
21 var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
22 mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
23 mem.set(u8, initial_state[secret_seed_length..], 0);
24 var self = Gimli{
25 .random = Random{ .fillFn = fill },
26 .state = std.crypto.core.Gimli.init(initial_state),
27 };
28 return self;
29}
30
31fn fill(r: *Random, buf: []u8) void {
32 const self = @fieldParentPtr(Gimli, "random", r);
33
34 if (buf.len != 0) {
35 self.state.squeeze(buf);
36 } else {
37 self.state.permute();
38 }
39 mem.set(u8, self.state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
40}
lib/std/rand/Isaac64.zig created+210
......@@ -0,0 +1,210 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
8//!
9//! Follows the general idea of the implementation from here with a few shortcuts.
10//! https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
11
12const std = @import("std");
13const Random = std.rand.Random;
14const mem = std.mem;
15const Isaac64 = @This();
16
17random: Random,
18
19r: [256]u64,
20m: [256]u64,
21a: u64,
22b: u64,
23c: u64,
24i: usize,
25
26pub fn init(init_s: u64) Isaac64 {
27 var isaac = Isaac64{
28 .random = Random{ .fillFn = fill },
29 .r = undefined,
30 .m = undefined,
31 .a = undefined,
32 .b = undefined,
33 .c = undefined,
34 .i = undefined,
35 };
36
37 // seed == 0 => same result as the unseeded reference implementation
38 isaac.seed(init_s, 1);
39 return isaac;
40}
41
42fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
43 const x = self.m[base + m1];
44 self.a = mix +% self.m[base + m2];
45
46 const y = self.a +% self.b +% self.m[@intCast(usize, (x >> 3) % self.m.len)];
47 self.m[base + m1] = y;
48
49 self.b = x +% self.m[@intCast(usize, (y >> 11) % self.m.len)];
50 self.r[self.r.len - 1 - base - m1] = self.b;
51}
52
53fn refill(self: *Isaac64) void {
54 const midpoint = self.r.len / 2;
55
56 self.c +%= 1;
57 self.b +%= self.c;
58
59 {
60 var i: usize = 0;
61 while (i < midpoint) : (i += 4) {
62 self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
63 self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint);
64 self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint);
65 self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint);
66 }
67 }
68
69 {
70 var i: usize = 0;
71 while (i < midpoint) : (i += 4) {
72 self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
73 self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0);
74 self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0);
75 self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0);
76 }
77 }
78
79 self.i = 0;
80}
81
82fn next(self: *Isaac64) u64 {
83 if (self.i >= self.r.len) {
84 self.refill();
85 }
86
87 const value = self.r[self.i];
88 self.i += 1;
89 return value;
90}
91
92fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
93 // We ignore the multi-pass requirement since we don't currently expose full access to
94 // seeding the self.m array completely.
95 mem.set(u64, self.m[0..], 0);
96 self.m[0] = init_s;
97
98 // prescrambled golden ratio constants
99 var a = [_]u64{
100 0x647c4677a2884b7c,
101 0xb9f8b322c73ac862,
102 0x8c0ea5053d4712a0,
103 0xb29b2e824a595524,
104 0x82f053db8355e0ce,
105 0x48fe4a0fa5a09315,
106 0xae985bf2cbfc89ed,
107 0x98f5704f6c44c0ab,
108 };
109
110 comptime var i: usize = 0;
111 inline while (i < rounds) : (i += 1) {
112 var j: usize = 0;
113 while (j < self.m.len) : (j += 8) {
114 comptime var x1: usize = 0;
115 inline while (x1 < 8) : (x1 += 1) {
116 a[x1] +%= self.m[j + x1];
117 }
118
119 a[0] -%= a[4];
120 a[5] ^= a[7] >> 9;
121 a[7] +%= a[0];
122 a[1] -%= a[5];
123 a[6] ^= a[0] << 9;
124 a[0] +%= a[1];
125 a[2] -%= a[6];
126 a[7] ^= a[1] >> 23;
127 a[1] +%= a[2];
128 a[3] -%= a[7];
129 a[0] ^= a[2] << 15;
130 a[2] +%= a[3];
131 a[4] -%= a[0];
132 a[1] ^= a[3] >> 14;
133 a[3] +%= a[4];
134 a[5] -%= a[1];
135 a[2] ^= a[4] << 20;
136 a[4] +%= a[5];
137 a[6] -%= a[2];
138 a[3] ^= a[5] >> 17;
139 a[5] +%= a[6];
140 a[7] -%= a[3];
141 a[4] ^= a[6] << 14;
142 a[6] +%= a[7];
143
144 comptime var x2: usize = 0;
145 inline while (x2 < 8) : (x2 += 1) {
146 self.m[j + x2] = a[x2];
147 }
148 }
149 }
150
151 mem.set(u64, self.r[0..], 0);
152 self.a = 0;
153 self.b = 0;
154 self.c = 0;
155 self.i = self.r.len; // trigger refill on first value
156}
157
158fn fill(r: *Random, buf: []u8) void {
159 const self = @fieldParentPtr(Isaac64, "random", r);
160
161 var i: usize = 0;
162 const aligned_len = buf.len - (buf.len & 7);
163
164 // Fill complete 64-byte segments
165 while (i < aligned_len) : (i += 8) {
166 var n = self.next();
167 comptime var j: usize = 0;
168 inline while (j < 8) : (j += 1) {
169 buf[i + j] = @truncate(u8, n);
170 n >>= 8;
171 }
172 }
173
174 // Fill trailing, ignoring excess (cut the stream).
175 if (i != buf.len) {
176 var n = self.next();
177 while (i < buf.len) : (i += 1) {
178 buf[i] = @truncate(u8, n);
179 n >>= 8;
180 }
181 }
182}
183
184test "isaac64 sequence" {
185 var r = Isaac64.init(0);
186
187 // from reference implementation
188 const seq = [_]u64{
189 0xf67dfba498e4937c,
190 0x84a5066a9204f380,
191 0xfee34bd5f5514dbb,
192 0x4d1664739b8f80d6,
193 0x8607459ab52a14aa,
194 0x0e78bc5a98529e49,
195 0xfe5332822ad13777,
196 0x556c27525e33d01a,
197 0x08643ca615f3149f,
198 0xd0771faf3cb04714,
199 0x30e86f68a37b008d,
200 0x3074ebc0488a3adf,
201 0x270645ea7a2790bc,
202 0x5601a0a8d3763c6a,
203 0x2f83071f53f325dd,
204 0xb9090f3d42d2d2ea,
205 };
206
207 for (seq) |s| {
208 std.testing.expect(s == r.next());
209 }
210}
lib/std/rand/Pcg.zig created+101
......@@ -0,0 +1,101 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! PCG32 - http://www.pcg-random.org/
8//!
9//! PRNG
10
11const std = @import("std");
12const Random = std.rand.Random;
13const Pcg = @This();
14
15const default_multiplier = 6364136223846793005;
16
17random: Random,
18
19s: u64,
20i: u64,
21
22pub fn init(init_s: u64) Pcg {
23 var pcg = Pcg{
24 .random = Random{ .fillFn = fill },
25 .s = undefined,
26 .i = undefined,
27 };
28
29 pcg.seed(init_s);
30 return pcg;
31}
32
33fn next(self: *Pcg) u32 {
34 const l = self.s;
35 self.s = l *% default_multiplier +% (self.i | 1);
36
37 const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
38 const rot = @intCast(u32, l >> 59);
39
40 return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31));
41}
42
43fn seed(self: *Pcg, init_s: u64) void {
44 // Pcg requires 128-bits of seed.
45 var gen = std.rand.SplitMix64.init(init_s);
46 self.seedTwo(gen.next(), gen.next());
47}
48
49fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void {
50 self.s = 0;
51 self.i = (init_s << 1) | 1;
52 self.s = self.s *% default_multiplier +% self.i;
53 self.s +%= init_i;
54 self.s = self.s *% default_multiplier +% self.i;
55}
56
57fn fill(r: *Random, buf: []u8) void {
58 const self = @fieldParentPtr(Pcg, "random", r);
59
60 var i: usize = 0;
61 const aligned_len = buf.len - (buf.len & 7);
62
63 // Complete 4 byte segments.
64 while (i < aligned_len) : (i += 4) {
65 var n = self.next();
66 comptime var j: usize = 0;
67 inline while (j < 4) : (j += 1) {
68 buf[i + j] = @truncate(u8, n);
69 n >>= 8;
70 }
71 }
72
73 // Remaining. (cuts the stream)
74 if (i != buf.len) {
75 var n = self.next();
76 while (i < buf.len) : (i += 1) {
77 buf[i] = @truncate(u8, n);
78 n >>= 4;
79 }
80 }
81}
82
83test "pcg sequence" {
84 var r = Pcg.init(0);
85 const s0: u64 = 0x9394bf54ce5d79de;
86 const s1: u64 = 0x84e9c579ef59bbf7;
87 r.seedTwo(s0, s1);
88
89 const seq = [_]u32{
90 2881561918,
91 3063928540,
92 1199791034,
93 2487695858,
94 1479648952,
95 3247963454,
96 };
97
98 for (seq) |s| {
99 std.testing.expect(s == r.next());
100 }
101}
lib/std/rand/Sfc64.zig created+108
......@@ -0,0 +1,108 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! Sfc64 pseudo-random number generator from Practically Random.
8//! Fastest engine of pracrand and smallest footprint.
9//! See http://pracrand.sourceforge.net/
10
11const std = @import("std");
12const Random = std.rand.Random;
13const math = std.math;
14const Sfc64 = @This();
15
16random: Random,
17
18a: u64 = undefined,
19b: u64 = undefined,
20c: u64 = undefined,
21counter: u64 = undefined,
22
23const Rotation = 24;
24const RightShift = 11;
25const LeftShift = 3;
26
27pub fn init(init_s: u64) Sfc64 {
28 var x = Sfc64{
29 .random = Random{ .fillFn = fill },
30 };
31
32 x.seed(init_s);
33 return x;
34}
35
36fn next(self: *Sfc64) u64 {
37 const tmp = self.a +% self.b +% self.counter;
38 self.counter += 1;
39 self.a = self.b ^ (self.b >> RightShift);
40 self.b = self.c +% (self.c << LeftShift);
41 self.c = math.rotl(u64, self.c, Rotation) +% tmp;
42 return tmp;
43}
44
45fn seed(self: *Sfc64, init_s: u64) void {
46 self.a = init_s;
47 self.b = init_s;
48 self.c = init_s;
49 self.counter = 1;
50 var i: u32 = 0;
51 while (i < 12) : (i += 1) {
52 _ = self.next();
53 }
54}
55
56fn fill(r: *Random, buf: []u8) void {
57 const self = @fieldParentPtr(Sfc64, "random", r);
58
59 var i: usize = 0;
60 const aligned_len = buf.len - (buf.len & 7);
61
62 // Complete 8 byte segments.
63 while (i < aligned_len) : (i += 8) {
64 var n = self.next();
65 comptime var j: usize = 0;
66 inline while (j < 8) : (j += 1) {
67 buf[i + j] = @truncate(u8, n);
68 n >>= 8;
69 }
70 }
71
72 // Remaining. (cuts the stream)
73 if (i != buf.len) {
74 var n = self.next();
75 while (i < buf.len) : (i += 1) {
76 buf[i] = @truncate(u8, n);
77 n >>= 8;
78 }
79 }
80}
81
82test "Sfc64 sequence" {
83 // Unfortunately there does not seem to be an official test sequence.
84 var r = Sfc64.init(0);
85
86 const seq = [_]u64{
87 0x3acfa029e3cc6041,
88 0xf5b6515bf2ee419c,
89 0x1259635894a29b61,
90 0xb6ae75395f8ebd6,
91 0x225622285ce302e2,
92 0x520d28611395cb21,
93 0xdb909c818901599d,
94 0x8ffd195365216f57,
95 0xe8c4ad5e258ac04a,
96 0x8f8ef2c89fdb63ca,
97 0xf9865b01d98d8e2f,
98 0x46555871a65d08ba,
99 0x66868677c6298fcd,
100 0x2ce15a7e6329f57d,
101 0xb2f1833ca91ca79,
102 0x4b0890ac9bf453ca,
103 };
104
105 for (seq) |s| {
106 std.testing.expectEqual(s, r.next());
107 }
108}
lib/std/rand/Xoroshiro128.zig created+133
......@@ -0,0 +1,133 @@
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2015-2020 Zig Contributors
3// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.
6
7//! Xoroshiro128+ - http://xoroshiro.di.unimi.it/
8//!
9//! PRNG
10
11const std = @import("std");
12const Random = std.rand.Random;
13const math = std.math;
14const Xoroshiro128 = @This();
15
16random: Random,
17
18s: [2]u64,
19
20pub fn init(init_s: u64) Xoroshiro128 {
21 var x = Xoroshiro128{
22 .random = Random{ .fillFn = fill },
23 .s = undefined,
24 };
25
26 x.seed(init_s);
27 return x;
28}
29
30fn next(self: *Xoroshiro128) u64 {
31 const s0 = self.s[0];
32 var s1 = self.s[1];
33 const r = s0 +% s1;
34
35 s1 ^= s0;
36 self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
37 self.s[1] = math.rotl(u64, s1, @as(u8, 36));
38
39 return r;
40}
41
42// Skip 2^64 places ahead in the sequence
43fn jump(self: *Xoroshiro128) void {
44 var s0: u64 = 0;
45 var s1: u64 = 0;
46
47 const table = [_]u64{
48 0xbeac0467eba5facb,
49 0xd86b048b86aa9922,
50 };
51
52 inline for (table) |entry| {
53 var b: usize = 0;
54 while (b < 64) : (b += 1) {
55 if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) {
56 s0 ^= self.s[0];
57 s1 ^= self.s[1];
58 }
59 _ = self.next();
60 }
61 }
62
63 self.s[0] = s0;
64 self.s[1] = s1;
65}
66
67pub fn seed(self: *Xoroshiro128, init_s: u64) void {
68 // Xoroshiro requires 128-bits of seed.
69 var gen = std.rand.SplitMix64.init(init_s);
70
71 self.s[0] = gen.next();
72 self.s[1] = gen.next();
73}
74
75fn fill(r: *Random, buf: []u8) void {
76 const self = @fieldParentPtr(Xoroshiro128, "random", r);
77
78 var i: usize = 0;
79 const aligned_len = buf.len - (buf.len & 7);
80
81 // Complete 8 byte segments.
82 while (i < aligned_len) : (i += 8) {
83 var n = self.next();
84 comptime var j: usize = 0;
85 inline while (j < 8) : (j += 1) {
86 buf[i + j] = @truncate(u8, n);
87 n >>= 8;
88 }
89 }
90
91 // Remaining. (cuts the stream)
92 if (i != buf.len) {
93 var n = self.next();
94 while (i < buf.len) : (i += 1) {
95 buf[i] = @truncate(u8, n);
96 n >>= 8;
97 }
98 }
99}
100
101test "xoroshiro sequence" {
102 var r = Xoroshiro128.init(0);
103 r.s[0] = 0xaeecf86f7878dd75;
104 r.s[1] = 0x01cd153642e72622;
105
106 const seq1 = [_]u64{
107 0xb0ba0da5bb600397,
108 0x18a08afde614dccc,
109 0xa2635b956a31b929,
110 0xabe633c971efa045,
111 0x9ac19f9706ca3cac,
112 0xf62b426578c1e3fb,
113 };
114
115 for (seq1) |s| {
116 std.testing.expect(s == r.next());
117 }
118
119 r.jump();
120
121 const seq2 = [_]u64{
122 0x95344a13556d3e22,
123 0xb4fb32dafa4d00df,
124 0xb2011d9ccdcfe2dd,
125 0x05679a9b2119b908,
126 0xa860a1da7c9cd8a0,
127 0x658a96efe3f86550,
128 };
129
130 for (seq2) |s| {
131 std.testing.expect(s == r.next());
132 }
133}
lib/std/start.zig+1
......@@ -10,6 +10,7 @@ const std = @import("std.zig");
1010const builtin = std.builtin;
1111const assert = std.debug.assert;
1212const uefi = std.os.uefi;
13const tlcsprng = @import("crypto/tlcsprng.zig");
1314
1415var argc_argv_ptr: [*]usize = undefined;
1516
lib/std/testing.zig+1-2
......@@ -303,8 +303,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
303303
304304pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
305305 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
306 std.crypto.randomBytes(&random_bytes) catch
307 @panic("unable to make tmp dir for testing: unable to get random bytes");
306 std.crypto.random.bytes(&random_bytes);
308307 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
309308 std.fs.base64_encoder.encode(&sub_path, &random_bytes);
310309
src/Compilation.zig+1-6
......@@ -74,7 +74,6 @@ zig_lib_directory: Directory,
7474local_cache_directory: Directory,
7575global_cache_directory: Directory,
7676libc_include_dir_list: []const []const u8,
77rand: *std.rand.Random,
7877
7978/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
8079/// and resolved before calling linker.flush().
......@@ -331,7 +330,6 @@ pub const InitOptions = struct {
331330 root_name: []const u8,
332331 root_pkg: ?*Package,
333332 output_mode: std.builtin.OutputMode,
334 rand: *std.rand.Random,
335333 dynamic_linker: ?[]const u8 = null,
336334 /// `null` means to not emit a binary file.
337335 emit_bin: ?EmitLoc,
......@@ -981,7 +979,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
981979 .self_exe_path = options.self_exe_path,
982980 .libc_include_dir_list = libc_dirs.libc_include_dir_list,
983981 .sanitize_c = sanitize_c,
984 .rand = options.rand,
985982 .clang_passthrough_mode = options.clang_passthrough_mode,
986983 .clang_preprocessor_mode = options.clang_preprocessor_mode,
987984 .verbose_cc = options.verbose_cc,
......@@ -1909,7 +1906,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
19091906
19101907pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
19111908 const s = std.fs.path.sep_str;
1912 const rand_int = comp.rand.int(u64);
1909 const rand_int = std.crypto.random.int(u64);
19131910 if (comp.local_cache_directory.path) |p| {
19141911 return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
19151912 } else {
......@@ -2778,7 +2775,6 @@ fn buildOutputFromZig(
27782775 .root_name = root_name,
27792776 .root_pkg = &root_pkg,
27802777 .output_mode = fixed_output_mode,
2781 .rand = comp.rand,
27822778 .libc_installation = comp.bin_file.options.libc_installation,
27832779 .emit_bin = emit_bin,
27842780 .optimize_mode = optimize_mode,
......@@ -3152,7 +3148,6 @@ pub fn build_crt_file(
31523148 .root_name = root_name,
31533149 .root_pkg = null,
31543150 .output_mode = output_mode,
3155 .rand = comp.rand,
31563151 .libc_installation = comp.bin_file.options.libc_installation,
31573152 .emit_bin = emit_bin,
31583153 .optimize_mode = comp.bin_file.options.optimize_mode,
src/glibc.zig-1
......@@ -936,7 +936,6 @@ fn buildSharedLib(
936936 .root_pkg = null,
937937 .output_mode = .Lib,
938938 .link_mode = .Dynamic,
939 .rand = comp.rand,
940939 .libc_installation = comp.bin_file.options.libc_installation,
941940 .emit_bin = emit_bin,
942941 .optimize_mode = comp.bin_file.options.optimize_mode,
src/libcxx.zig-2
......@@ -162,7 +162,6 @@ pub fn buildLibCXX(comp: *Compilation) !void {
162162 .root_name = root_name,
163163 .root_pkg = null,
164164 .output_mode = output_mode,
165 .rand = comp.rand,
166165 .libc_installation = comp.bin_file.options.libc_installation,
167166 .emit_bin = emit_bin,
168167 .optimize_mode = comp.bin_file.options.optimize_mode,
......@@ -281,7 +280,6 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
281280 .root_name = root_name,
282281 .root_pkg = null,
283282 .output_mode = output_mode,
284 .rand = comp.rand,
285283 .libc_installation = comp.bin_file.options.libc_installation,
286284 .emit_bin = emit_bin,
287285 .optimize_mode = comp.bin_file.options.optimize_mode,
src/libunwind.zig-1
......@@ -95,7 +95,6 @@ pub fn buildStaticLib(comp: *Compilation) !void {
9595 .root_name = root_name,
9696 .root_pkg = null,
9797 .output_mode = output_mode,
98 .rand = comp.rand,
9998 .libc_installation = comp.bin_file.options.libc_installation,
10099 .emit_bin = emit_bin,
101100 .optimize_mode = comp.bin_file.options.optimize_mode,
src/main.zig-15
......@@ -1632,13 +1632,6 @@ fn buildOutputType(
16321632 };
16331633 defer zig_lib_directory.handle.close();
16341634
1635 const random_seed = blk: {
1636 var random_seed: u64 = undefined;
1637 try std.crypto.randomBytes(mem.asBytes(&random_seed));
1638 break :blk random_seed;
1639 };
1640 var default_prng = std.rand.DefaultPrng.init(random_seed);
1641
16421635 var libc_installation: ?LibCInstallation = null;
16431636 defer if (libc_installation) |*l| l.deinit(gpa);
16441637
......@@ -1754,7 +1747,6 @@ fn buildOutputType(
17541747 .single_threaded = single_threaded,
17551748 .function_sections = function_sections,
17561749 .self_exe_path = self_exe_path,
1757 .rand = &default_prng.random,
17581750 .clang_passthrough_mode = arg_mode != .build,
17591751 .clang_preprocessor_mode = clang_preprocessor_mode,
17601752 .version = optional_version,
......@@ -2420,12 +2412,6 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
24202412 .directory = null, // Use the local zig-cache.
24212413 .basename = exe_basename,
24222414 };
2423 const random_seed = blk: {
2424 var random_seed: u64 = undefined;
2425 try std.crypto.randomBytes(mem.asBytes(&random_seed));
2426 break :blk random_seed;
2427 };
2428 var default_prng = std.rand.DefaultPrng.init(random_seed);
24292415 const comp = Compilation.create(gpa, .{
24302416 .zig_lib_directory = zig_lib_directory,
24312417 .local_cache_directory = local_cache_directory,
......@@ -2441,7 +2427,6 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
24412427 .emit_h = null,
24422428 .optimize_mode = .Debug,
24432429 .self_exe_path = self_exe_path,
2444 .rand = &default_prng.random,
24452430 }) catch |err| {
24462431 fatal("unable to create compilation: {}", .{@errorName(err)});
24472432 };
src/musl.zig-1
......@@ -200,7 +200,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
200200 .root_pkg = null,
201201 .output_mode = .Lib,
202202 .link_mode = .Dynamic,
203 .rand = comp.rand,
204203 .libc_installation = comp.bin_file.options.libc_installation,
205204 .emit_bin = Compilation.EmitLoc{ .directory = null, .basename = "libc.so" },
206205 .optimize_mode = comp.bin_file.options.optimize_mode,
src/test.zig+1-10
......@@ -467,13 +467,6 @@ pub const TestContext = struct {
467467 defer zig_lib_directory.handle.close();
468468 defer std.testing.allocator.free(zig_lib_directory.path.?);
469469
470 const random_seed = blk: {
471 var random_seed: u64 = undefined;
472 try std.crypto.randomBytes(std.mem.asBytes(&random_seed));
473 break :blk random_seed;
474 };
475 var default_prng = std.rand.DefaultPrng.init(random_seed);
476
477470 for (self.cases.items) |case| {
478471 if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch)
479472 continue;
......@@ -487,7 +480,7 @@ pub const TestContext = struct {
487480 progress.initial_delay_ns = 0;
488481 progress.refresh_rate_ns = 0;
489482
490 try self.runOneCase(std.testing.allocator, &prg_node, case, zig_lib_directory, &default_prng.random);
483 try self.runOneCase(std.testing.allocator, &prg_node, case, zig_lib_directory);
491484 }
492485 }
493486
......@@ -497,7 +490,6 @@ pub const TestContext = struct {
497490 root_node: *std.Progress.Node,
498491 case: Case,
499492 zig_lib_directory: Compilation.Directory,
500 rand: *std.rand.Random,
501493 ) !void {
502494 const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
503495 const target = target_info.target;
......@@ -547,7 +539,6 @@ pub const TestContext = struct {
547539 .local_cache_directory = zig_cache_directory,
548540 .global_cache_directory = zig_cache_directory,
549541 .zig_lib_directory = zig_lib_directory,
550 .rand = rand,
551542 .root_name = "test_case",
552543 .target = target,
553544 // TODO: support tests for object file building, and library builds
test/stack_traces.zig+3-3
......@@ -282,10 +282,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
282282 \\source.zig:10:8: [address] in main (test)
283283 \\ foo();
284284 \\ ^
285 \\start.zig:341:29: [address] in std.start.posixCallMainAndExit (test)
285 \\start.zig:342:29: [address] in std.start.posixCallMainAndExit (test)
286286 \\ return root.main();
287287 \\ ^
288 \\start.zig:162:5: [address] in std.start._start (test)
288 \\start.zig:163:5: [address] in std.start._start (test)
289289 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
290290 \\ ^
291291 \\
......@@ -294,7 +294,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
294294 switch (std.Target.current.cpu.arch) {
295295 .aarch64 => "", // TODO disabled; results in segfault
296296 else =>
297 \\start.zig:162:5: [address] in std.start._start (test)
297 \\start.zig:163:5: [address] in std.start._start (test)
298298 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
299299 \\ ^
300300 \\
test/standalone/guess_number/main.zig+1-9
......@@ -9,15 +9,7 @@ pub fn main() !void {
99
1010 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});
1111
12 var seed_bytes: [@sizeOf(u64)]u8 = undefined;
13 std.crypto.randomBytes(seed_bytes[0..]) catch |err| {
14 std.debug.warn("unable to seed random number generator: {}", .{err});
15 return err;
16 };
17 const seed = std.mem.readIntNative(u64, &seed_bytes);
18 var prng = std.rand.DefaultPrng.init(seed);
19
20 const answer = prng.random.intRangeLessThan(u8, 0, 100) + 1;
12 const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;
2113
2214 while (true) {
2315 try stdout.print("\nGuess a number between 1 and 100: ", .{});