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...@@ -264,6 +264,11 @@ pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: us
264pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;264pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
265pub extern "c" fn pthread_self() pthread_t;265pub extern "c" fn pthread_self() pthread_t;
266pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;266pub 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
268pub extern "c" fn kqueue() c_int;273pub extern "c" fn kqueue() c_int;
269pub extern "c" fn kevent(274pub 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...@@ -106,6 +106,12 @@ pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *con
106pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;106pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
107pub extern "c" fn malloc_usable_size(?*const c_void) usize;107pub 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
109pub const pthread_attr_t = extern struct {115pub const pthread_attr_t = extern struct {
110 __size: [56]u8,116 __size: [56]u8,
111 __align: c_long,117 __align: c_long,
lib/std/crypto.zig+10-1
...@@ -134,8 +134,10 @@ pub const nacl = struct {...@@ -134,8 +134,10 @@ pub const nacl = struct {
134134
135pub const utils = @import("crypto/utils.zig");135pub 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
137const std = @import("std.zig");140const std = @import("std.zig");
138pub const randomBytes = std.os.getrandom;
139141
140test "crypto" {142test "crypto" {
141 inline for (std.meta.declarations(@This())) |decl| {143 inline for (std.meta.declarations(@This())) |decl| {
...@@ -178,6 +180,13 @@ test "crypto" {...@@ -178,6 +180,13 @@ test "crypto" {
178 _ = @import("crypto/25519/ristretto255.zig");180 _ = @import("crypto/25519/ristretto255.zig");
179}181}
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
181test "issue #4532: no index out of bounds" {190test "issue #4532: no index out of bounds" {
182 const types = [_]type{191 const types = [_]type{
183 hash.Md5,192 hash.Md5,
lib/std/crypto/25519/ed25519.zig+4-4
...@@ -43,7 +43,7 @@ pub const Ed25519 = struct {...@@ -43,7 +43,7 @@ pub const Ed25519 = struct {
43 pub fn create(seed: ?[seed_length]u8) !KeyPair {43 pub fn create(seed: ?[seed_length]u8) !KeyPair {
44 const ss = seed orelse ss: {44 const ss = seed orelse ss: {
45 var random_seed: [seed_length]u8 = undefined;45 var random_seed: [seed_length]u8 = undefined;
46 try crypto.randomBytes(&random_seed);46 crypto.random.bytes(&random_seed);
47 break :ss random_seed;47 break :ss random_seed;
48 };48 };
49 var az: [Sha512.digest_length]u8 = undefined;49 var az: [Sha512.digest_length]u8 = undefined;
...@@ -179,7 +179,7 @@ pub const Ed25519 = struct {...@@ -179,7 +179,7 @@ pub const Ed25519 = struct {
179179
180 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;180 var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
181 for (z_batch) |*z| {181 for (z_batch) |*z| {
182 try std.crypto.randomBytes(z[0..16]);182 std.crypto.random.bytes(z[0..16]);
183 mem.set(u8, z[16..], 0);183 mem.set(u8, z[16..], 0);
184 }184 }
185185
...@@ -232,8 +232,8 @@ test "ed25519 batch verification" {...@@ -232,8 +232,8 @@ test "ed25519 batch verification" {
232 const key_pair = try Ed25519.KeyPair.create(null);232 const key_pair = try Ed25519.KeyPair.create(null);
233 var msg1: [32]u8 = undefined;233 var msg1: [32]u8 = undefined;
234 var msg2: [32]u8 = undefined;234 var msg2: [32]u8 = undefined;
235 try std.crypto.randomBytes(&msg1);235 std.crypto.random.bytes(&msg1);
236 try std.crypto.randomBytes(&msg2);236 std.crypto.random.bytes(&msg2);
237 const sig1 = try Ed25519.sign(&msg1, key_pair, null);237 const sig1 = try Ed25519.sign(&msg1, key_pair, null);
238 const sig2 = try Ed25519.sign(&msg2, key_pair, null);238 const sig2 = try Ed25519.sign(&msg2, key_pair, null);
239 var signature_batch = [_]Ed25519.BatchElement{239 var signature_batch = [_]Ed25519.BatchElement{
lib/std/crypto/25519/edwards25519.zig+2-2
...@@ -484,8 +484,8 @@ test "edwards25519 packing/unpacking" {...@@ -484,8 +484,8 @@ test "edwards25519 packing/unpacking" {
484test "edwards25519 point addition/substraction" {484test "edwards25519 point addition/substraction" {
485 var s1: [32]u8 = undefined;485 var s1: [32]u8 = undefined;
486 var s2: [32]u8 = undefined;486 var s2: [32]u8 = undefined;
487 try std.crypto.randomBytes(&s1);487 std.crypto.random.bytes(&s1);
488 try std.crypto.randomBytes(&s2);488 std.crypto.random.bytes(&s2);
489 const p = try Edwards25519.basePoint.clampedMul(s1);489 const p = try Edwards25519.basePoint.clampedMul(s1);
490 const q = try Edwards25519.basePoint.clampedMul(s2);490 const q = try Edwards25519.basePoint.clampedMul(s2);
491 const r = p.add(q).add(q).sub(q).sub(q);491 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 {...@@ -34,7 +34,7 @@ pub const X25519 = struct {
34 pub fn create(seed: ?[seed_length]u8) !KeyPair {34 pub fn create(seed: ?[seed_length]u8) !KeyPair {
35 const sk = seed orelse sk: {35 const sk = seed orelse sk: {
36 var random_seed: [seed_length]u8 = undefined;36 var random_seed: [seed_length]u8 = undefined;
37 try crypto.randomBytes(&random_seed);37 crypto.random.bytes(&random_seed);
38 break :sk random_seed;38 break :sk random_seed;
39 };39 };
40 var kp: KeyPair = undefined;40 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)...@@ -262,7 +262,7 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
262/// and then use the resulting hash as the password parameter for bcrypt.262/// and then use the resulting hash as the password parameter for bcrypt.
263pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {263pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {
264 var salt: [salt_length]u8 = undefined;264 var salt: [salt_length]u8 = undefined;
265 try crypto.randomBytes(&salt);265 crypto.random.bytes(&salt);
266 return strHashInternal(password, rounds_log, salt);266 return strHashInternal(password, rounds_log, salt);
267}267}
268268
...@@ -283,7 +283,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {...@@ -283,7 +283,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {
283283
284test "bcrypt codec" {284test "bcrypt codec" {
285 var salt: [salt_length]u8 = undefined;285 var salt: [salt_length]u8 = undefined;
286 try crypto.randomBytes(&salt);286 crypto.random.bytes(&salt);
287 var salt_str: [salt_str_length]u8 = undefined;287 var salt_str: [salt_str_length]u8 = undefined;
288 Codec.encode(salt_str[0..], salt[0..]);288 Codec.encode(salt_str[0..], salt[0..]);
289 var salt2: [salt_length]u8 = undefined;289 var salt2: [salt_length]u8 = undefined;
lib/std/crypto/salsa20.zig+9-9
...@@ -571,9 +571,9 @@ test "xsalsa20poly1305" {...@@ -571,9 +571,9 @@ test "xsalsa20poly1305" {
571 var key: [XSalsa20Poly1305.key_length]u8 = undefined;571 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
572 var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;572 var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;
573 var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;573 var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;
574 try crypto.randomBytes(&msg);574 crypto.random.bytes(&msg);
575 try crypto.randomBytes(&key);575 crypto.random.bytes(&key);
576 try crypto.randomBytes(&nonce);576 crypto.random.bytes(&nonce);
577577
578 XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);578 XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);
579 try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);579 try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);
...@@ -585,9 +585,9 @@ test "xsalsa20poly1305 secretbox" {...@@ -585,9 +585,9 @@ test "xsalsa20poly1305 secretbox" {
585 var key: [XSalsa20Poly1305.key_length]u8 = undefined;585 var key: [XSalsa20Poly1305.key_length]u8 = undefined;
586 var nonce: [Box.nonce_length]u8 = undefined;586 var nonce: [Box.nonce_length]u8 = undefined;
587 var boxed: [msg.len + Box.tag_length]u8 = undefined;587 var boxed: [msg.len + Box.tag_length]u8 = undefined;
588 try crypto.randomBytes(&msg);588 crypto.random.bytes(&msg);
589 try crypto.randomBytes(&key);589 crypto.random.bytes(&key);
590 try crypto.randomBytes(&nonce);590 crypto.random.bytes(&nonce);
591591
592 SecretBox.seal(boxed[0..], msg[0..], nonce, key);592 SecretBox.seal(boxed[0..], msg[0..], nonce, key);
593 try SecretBox.open(msg2[0..], boxed[0..], nonce, key);593 try SecretBox.open(msg2[0..], boxed[0..], nonce, key);
...@@ -598,8 +598,8 @@ test "xsalsa20poly1305 box" {...@@ -598,8 +598,8 @@ test "xsalsa20poly1305 box" {
598 var msg2: [msg.len]u8 = undefined;598 var msg2: [msg.len]u8 = undefined;
599 var nonce: [Box.nonce_length]u8 = undefined;599 var nonce: [Box.nonce_length]u8 = undefined;
600 var boxed: [msg.len + Box.tag_length]u8 = undefined;600 var boxed: [msg.len + Box.tag_length]u8 = undefined;
601 try crypto.randomBytes(&msg);601 crypto.random.bytes(&msg);
602 try crypto.randomBytes(&nonce);602 crypto.random.bytes(&nonce);
603603
604 var kp1 = try Box.KeyPair.create(null);604 var kp1 = try Box.KeyPair.create(null);
605 var kp2 = try Box.KeyPair.create(null);605 var kp2 = try Box.KeyPair.create(null);
...@@ -611,7 +611,7 @@ test "xsalsa20poly1305 sealedbox" {...@@ -611,7 +611,7 @@ test "xsalsa20poly1305 sealedbox" {
611 var msg: [100]u8 = undefined;611 var msg: [100]u8 = undefined;
612 var msg2: [msg.len]u8 = undefined;612 var msg2: [msg.len]u8 = undefined;
613 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;613 var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
614 try crypto.randomBytes(&msg);614 crypto.random.bytes(&msg);
615615
616 var kp = try Box.KeyPair.create(null);616 var kp = try Box.KeyPair.create(null);
617 try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);617 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 {...@@ -51,8 +51,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
51test "crypto.utils.timingSafeEql" {51test "crypto.utils.timingSafeEql" {
52 var a: [100]u8 = undefined;52 var a: [100]u8 = undefined;
53 var b: [100]u8 = undefined;53 var b: [100]u8 = undefined;
54 try std.crypto.randomBytes(a[0..]);54 std.crypto.random.bytes(a[0..]);
55 try std.crypto.randomBytes(b[0..]);55 std.crypto.random.bytes(b[0..]);
56 testing.expect(!timingSafeEql([100]u8, a, b));56 testing.expect(!timingSafeEql([100]u8, a, b));
57 mem.copy(u8, a[0..], b[0..]);57 mem.copy(u8, a[0..], b[0..]);
58 testing.expect(timingSafeEql([100]u8, a, b));58 testing.expect(timingSafeEql([100]u8, a, b));
...@@ -61,8 +61,8 @@ test "crypto.utils.timingSafeEql" {...@@ -61,8 +61,8 @@ test "crypto.utils.timingSafeEql" {
61test "crypto.utils.timingSafeEql (vectors)" {61test "crypto.utils.timingSafeEql (vectors)" {
62 var a: [100]u8 = undefined;62 var a: [100]u8 = undefined;
63 var b: [100]u8 = undefined;63 var b: [100]u8 = undefined;
64 try std.crypto.randomBytes(a[0..]);64 std.crypto.random.bytes(a[0..]);
65 try std.crypto.randomBytes(b[0..]);65 std.crypto.random.bytes(b[0..]);
66 const v1: std.meta.Vector(100, u8) = a;66 const v1: std.meta.Vector(100, u8) = a;
67 const v2: std.meta.Vector(100, u8) = b;67 const v2: std.meta.Vector(100, u8) = b;
68 testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2));68 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:...@@ -82,7 +82,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
82 mem.copy(u8, tmp_path[0..], dirname);82 mem.copy(u8, tmp_path[0..], dirname);
83 tmp_path[dirname.len] = path.sep;83 tmp_path[dirname.len] = path.sep;
84 while (true) {84 while (true) {
85 try crypto.randomBytes(rand_buf[0..]);85 crypto.random.bytes(rand_buf[0..]);
86 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);86 base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
8787
88 if (cwd().symLink(existing_path, tmp_path, .{})) {88 if (cwd().symLink(existing_path, tmp_path, .{})) {
...@@ -157,7 +157,7 @@ pub const AtomicFile = struct {...@@ -157,7 +157,7 @@ pub const AtomicFile = struct {
157 tmp_path_buf[base64.Base64Encoder.calcSize(RANDOM_BYTES)] = 0;157 tmp_path_buf[base64.Base64Encoder.calcSize(RANDOM_BYTES)] = 0;
158158
159 while (true) {159 while (true) {
160 try crypto.randomBytes(rand_buf[0..]);160 crypto.random.bytes(rand_buf[0..]);
161 base64_encoder.encode(&tmp_path_buf, &rand_buf);161 base64_encoder.encode(&tmp_path_buf, &rand_buf);
162162
163 const file = dir.createFile(163 const file = dir.createFile(
lib/std/meta.zig+8
...@@ -9,6 +9,7 @@ const debug = std.debug;...@@ -9,6 +9,7 @@ const debug = std.debug;
9const mem = std.mem;9const mem = std.mem;
10const math = std.math;10const math = std.math;
11const testing = std.testing;11const testing = std.testing;
12const root = @import("root");
1213
13pub const trait = @import("meta/trait.zig");14pub const trait = @import("meta/trait.zig");
14pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;15pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
...@@ -1085,3 +1086,10 @@ test "Tuple" {...@@ -1085,3 +1086,10 @@ test "Tuple" {
1085 TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));1086 TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
1086 TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void }));1087 TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void }));
1087}1088}
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...@@ -5845,3 +5845,51 @@ pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void
5845 else => |err| return unexpectedErrno(err),5845 else => |err| return unexpectedErrno(err),
5846 }5846 }
5847}5847}
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 {...@@ -2045,3 +2045,25 @@ pub const rlimit = extern struct {
2045 /// Hard limit2045 /// Hard limit
2046 max: rlim_t,2046 max: rlim_t,
2047};2047};
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,...@@ -1351,6 +1351,10 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit,
1351 );1351 );
1352}1352}
13531353
1354pub fn madvise(address: [*]u8, len: usize, advice: u32) usize {
1355 return syscall3(.madvise, @ptrToInt(address), len, advice);
1356}
1357
1354test "" {1358test "" {
1355 if (builtin.os.tag == .linux) {1359 if (builtin.os.tag == .linux) {
1356 _ = @import("linux/test.zig");1360 _ = @import("linux/test.zig");
lib/std/os/linux/tls.zig+24-15
...@@ -327,34 +327,43 @@ pub fn prepareTLS(area: []u8) usize {...@@ -327,34 +327,43 @@ pub fn prepareTLS(area: []u8) usize {
327 if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset;327 if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset;
328}328}
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
332pub fn initStaticTLS() void {340pub fn initStaticTLS() void {
333 initTLS();341 initTLS();
334342
335 const alloc_tls_area: []u8 = blk: {343 const tls_area = blk: {
336 const full_alloc_size = tls_image.alloc_size + tls_image.alloc_align - 1;
337
338 // Fast path for the common case where the TLS data is really small,344 // Fast path for the common case where the TLS data is really small,
339 // avoid an allocation and use our local buffer345 // avoid an allocation and use our local buffer.
340 if (full_alloc_size < main_thread_tls_buffer.len)346 if (tls_image.alloc_align <= mem.page_size and
341 break :blk main_thread_tls_buffer[0..];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(
344 null,353 null,
345 full_alloc_size,354 tls_image.alloc_size + tls_image.alloc_align - 1,
346 os.PROT_READ | os.PROT_WRITE,355 os.PROT_READ | os.PROT_WRITE,
347 os.MAP_PRIVATE | os.MAP_ANONYMOUS,356 os.MAP_PRIVATE | os.MAP_ANONYMOUS,
348 -1,357 -1,
349 0,358 0,
350 ) catch os.abort();359 ) catch os.abort();
351 };
352360
353 // Make sure the slice is correctly aligned361 // Make sure the slice is correctly aligned.
354 const begin_addr = @ptrToInt(alloc_tls_area.ptr);362 const begin_addr = @ptrToInt(alloc_tls_area.ptr);
355 const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align);363 const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align);
356 const start = begin_aligned_addr - begin_addr;364 const start = begin_aligned_addr - begin_addr;
357 const tls_area = alloc_tls_area[start .. start + tls_image.alloc_size];365 break :blk alloc_tls_area[start .. start + tls_image.alloc_size];
366 };
358367
359 const tp_value = prepareTLS(tls_area);368 const tp_value = prepareTLS(tls_area);
360 setThreadPointer(tp_value);369 setThreadPointer(tp_value);
lib/std/rand.zig+13-566
...@@ -4,19 +4,11 @@...@@ -4,19 +4,11 @@
4// The MIT license requires this copyright notice to be included in all copies4// The MIT license requires this copyright notice to be included in all copies
5// and substantial portions of the software.5// and substantial portions of the software.
66
7//! The engines provided here should be initialized from an external source. For now, randomBytes7//! The engines provided here should be initialized from an external source.
8//! from the crypto package is the most suitable. Be sure to use a CSPRNG when required, otherwise using8//! For a thread-local cryptographically secure pseudo random number generator,
9//! a normal PRNG will be faster and use substantially less stack space.9//! use `std.crypto.random`.
10//!10//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
11//! ```11//! be faster and use substantially less stack space.
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//! ```
20//!12//!
21//! TODO(tiehuis): Benchmark these against other reference implementations.13//! TODO(tiehuis): Benchmark these against other reference implementations.
2214
...@@ -36,6 +28,12 @@ pub const DefaultPrng = Xoroshiro128;...@@ -36,6 +28,12 @@ pub const DefaultPrng = Xoroshiro128;
36/// Cryptographically secure random numbers.28/// Cryptographically secure random numbers.
37pub const DefaultCsprng = Gimli;29pub 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
39pub const Random = struct {37pub const Random = struct {
40 fillFn: fn (r: *Random, buf: []u8) void,38 fillFn: fn (r: *Random, buf: []u8) void,
4139
...@@ -491,7 +489,7 @@ test "Random Biased" {...@@ -491,7 +489,7 @@ test "Random Biased" {
491//489//
492// The number of cycles is thus limited to 64-bits regardless of the engine, but this490// The number of cycles is thus limited to 64-bits regardless of the engine, but this
493// is still plenty for practical purposes.491// is still plenty for practical purposes.
494const SplitMix64 = struct {492pub const SplitMix64 = struct {
495 s: u64,493 s: u64,
496494
497 pub fn init(seed: u64) SplitMix64 {495 pub fn init(seed: u64) SplitMix64 {
...@@ -525,557 +523,6 @@ test "splitmix64 sequence" {...@@ -525,557 +523,6 @@ test "splitmix64 sequence" {
525 }523 }
526}524}
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
1079// Actual Random helper function tests, pcg engine is assumed correct.526// Actual Random helper function tests, pcg engine is assumed correct.
1080test "Random float" {527test "Random float" {
1081 var prng = DefaultPrng.init(0);528 var prng = DefaultPrng.init(0);
...@@ -1147,7 +594,7 @@ fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void {...@@ -1147,7 +594,7 @@ fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void {
1147594
1148test "CSPRNG" {595test "CSPRNG" {
1149 var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;596 var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
1150 try std.crypto.randomBytes(&secret_seed);597 std.crypto.random.bytes(&secret_seed);
1151 var csprng = DefaultCsprng.init(secret_seed);598 var csprng = DefaultCsprng.init(secret_seed);
1152 const a = csprng.random.int(u64);599 const a = csprng.random.int(u64);
1153 const b = csprng.random.int(u64);600 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");...@@ -10,6 +10,7 @@ const std = @import("std.zig");
10const builtin = std.builtin;10const builtin = std.builtin;
11const assert = std.debug.assert;11const assert = std.debug.assert;
12const uefi = std.os.uefi;12const uefi = std.os.uefi;
13const tlcsprng = @import("crypto/tlcsprng.zig");
1314
14var argc_argv_ptr: [*]usize = undefined;15var argc_argv_ptr: [*]usize = undefined;
1516
lib/std/testing.zig+1-2
...@@ -303,8 +303,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {...@@ -303,8 +303,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
303303
304pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {304pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
305 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;305 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
306 std.crypto.randomBytes(&random_bytes) catch306 std.crypto.random.bytes(&random_bytes);
307 @panic("unable to make tmp dir for testing: unable to get random bytes");
308 var sub_path: [TmpDir.sub_path_len]u8 = undefined;307 var sub_path: [TmpDir.sub_path_len]u8 = undefined;
309 std.fs.base64_encoder.encode(&sub_path, &random_bytes);308 std.fs.base64_encoder.encode(&sub_path, &random_bytes);
310309
src/Compilation.zig+1-6
...@@ -74,7 +74,6 @@ zig_lib_directory: Directory,...@@ -74,7 +74,6 @@ zig_lib_directory: Directory,
74local_cache_directory: Directory,74local_cache_directory: Directory,
75global_cache_directory: Directory,75global_cache_directory: Directory,
76libc_include_dir_list: []const []const u8,76libc_include_dir_list: []const []const u8,
77rand: *std.rand.Random,
7877
79/// Populated when we build the libc++ static library. A Job to build this is placed in the queue78/// Populated when we build the libc++ static library. A Job to build this is placed in the queue
80/// and resolved before calling linker.flush().79/// and resolved before calling linker.flush().
...@@ -331,7 +330,6 @@ pub const InitOptions = struct {...@@ -331,7 +330,6 @@ pub const InitOptions = struct {
331 root_name: []const u8,330 root_name: []const u8,
332 root_pkg: ?*Package,331 root_pkg: ?*Package,
333 output_mode: std.builtin.OutputMode,332 output_mode: std.builtin.OutputMode,
334 rand: *std.rand.Random,
335 dynamic_linker: ?[]const u8 = null,333 dynamic_linker: ?[]const u8 = null,
336 /// `null` means to not emit a binary file.334 /// `null` means to not emit a binary file.
337 emit_bin: ?EmitLoc,335 emit_bin: ?EmitLoc,
...@@ -981,7 +979,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -981,7 +979,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
981 .self_exe_path = options.self_exe_path,979 .self_exe_path = options.self_exe_path,
982 .libc_include_dir_list = libc_dirs.libc_include_dir_list,980 .libc_include_dir_list = libc_dirs.libc_include_dir_list,
983 .sanitize_c = sanitize_c,981 .sanitize_c = sanitize_c,
984 .rand = options.rand,
985 .clang_passthrough_mode = options.clang_passthrough_mode,982 .clang_passthrough_mode = options.clang_passthrough_mode,
986 .clang_preprocessor_mode = options.clang_preprocessor_mode,983 .clang_preprocessor_mode = options.clang_preprocessor_mode,
987 .verbose_cc = options.verbose_cc,984 .verbose_cc = options.verbose_cc,
...@@ -1909,7 +1906,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *...@@ -1909,7 +1906,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_comp_progress_node: *
19091906
1910pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {1907pub fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
1911 const s = std.fs.path.sep_str;1908 const s = std.fs.path.sep_str;
1912 const rand_int = comp.rand.int(u64);1909 const rand_int = std.crypto.random.int(u64);
1913 if (comp.local_cache_directory.path) |p| {1910 if (comp.local_cache_directory.path) |p| {
1914 return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });1911 return std.fmt.allocPrint(arena, "{}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
1915 } else {1912 } else {
...@@ -2778,7 +2775,6 @@ fn buildOutputFromZig(...@@ -2778,7 +2775,6 @@ fn buildOutputFromZig(
2778 .root_name = root_name,2775 .root_name = root_name,
2779 .root_pkg = &root_pkg,2776 .root_pkg = &root_pkg,
2780 .output_mode = fixed_output_mode,2777 .output_mode = fixed_output_mode,
2781 .rand = comp.rand,
2782 .libc_installation = comp.bin_file.options.libc_installation,2778 .libc_installation = comp.bin_file.options.libc_installation,
2783 .emit_bin = emit_bin,2779 .emit_bin = emit_bin,
2784 .optimize_mode = optimize_mode,2780 .optimize_mode = optimize_mode,
...@@ -3152,7 +3148,6 @@ pub fn build_crt_file(...@@ -3152,7 +3148,6 @@ pub fn build_crt_file(
3152 .root_name = root_name,3148 .root_name = root_name,
3153 .root_pkg = null,3149 .root_pkg = null,
3154 .output_mode = output_mode,3150 .output_mode = output_mode,
3155 .rand = comp.rand,
3156 .libc_installation = comp.bin_file.options.libc_installation,3151 .libc_installation = comp.bin_file.options.libc_installation,
3157 .emit_bin = emit_bin,3152 .emit_bin = emit_bin,
3158 .optimize_mode = comp.bin_file.options.optimize_mode,3153 .optimize_mode = comp.bin_file.options.optimize_mode,
src/glibc.zig-1
...@@ -936,7 +936,6 @@ fn buildSharedLib(...@@ -936,7 +936,6 @@ fn buildSharedLib(
936 .root_pkg = null,936 .root_pkg = null,
937 .output_mode = .Lib,937 .output_mode = .Lib,
938 .link_mode = .Dynamic,938 .link_mode = .Dynamic,
939 .rand = comp.rand,
940 .libc_installation = comp.bin_file.options.libc_installation,939 .libc_installation = comp.bin_file.options.libc_installation,
941 .emit_bin = emit_bin,940 .emit_bin = emit_bin,
942 .optimize_mode = comp.bin_file.options.optimize_mode,941 .optimize_mode = comp.bin_file.options.optimize_mode,
src/libcxx.zig-2
...@@ -162,7 +162,6 @@ pub fn buildLibCXX(comp: *Compilation) !void {...@@ -162,7 +162,6 @@ pub fn buildLibCXX(comp: *Compilation) !void {
162 .root_name = root_name,162 .root_name = root_name,
163 .root_pkg = null,163 .root_pkg = null,
164 .output_mode = output_mode,164 .output_mode = output_mode,
165 .rand = comp.rand,
166 .libc_installation = comp.bin_file.options.libc_installation,165 .libc_installation = comp.bin_file.options.libc_installation,
167 .emit_bin = emit_bin,166 .emit_bin = emit_bin,
168 .optimize_mode = comp.bin_file.options.optimize_mode,167 .optimize_mode = comp.bin_file.options.optimize_mode,
...@@ -281,7 +280,6 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {...@@ -281,7 +280,6 @@ pub fn buildLibCXXABI(comp: *Compilation) !void {
281 .root_name = root_name,280 .root_name = root_name,
282 .root_pkg = null,281 .root_pkg = null,
283 .output_mode = output_mode,282 .output_mode = output_mode,
284 .rand = comp.rand,
285 .libc_installation = comp.bin_file.options.libc_installation,283 .libc_installation = comp.bin_file.options.libc_installation,
286 .emit_bin = emit_bin,284 .emit_bin = emit_bin,
287 .optimize_mode = comp.bin_file.options.optimize_mode,285 .optimize_mode = comp.bin_file.options.optimize_mode,
src/libunwind.zig-1
...@@ -95,7 +95,6 @@ pub fn buildStaticLib(comp: *Compilation) !void {...@@ -95,7 +95,6 @@ pub fn buildStaticLib(comp: *Compilation) !void {
95 .root_name = root_name,95 .root_name = root_name,
96 .root_pkg = null,96 .root_pkg = null,
97 .output_mode = output_mode,97 .output_mode = output_mode,
98 .rand = comp.rand,
99 .libc_installation = comp.bin_file.options.libc_installation,98 .libc_installation = comp.bin_file.options.libc_installation,
100 .emit_bin = emit_bin,99 .emit_bin = emit_bin,
101 .optimize_mode = comp.bin_file.options.optimize_mode,100 .optimize_mode = comp.bin_file.options.optimize_mode,
src/main.zig-15
...@@ -1632,13 +1632,6 @@ fn buildOutputType(...@@ -1632,13 +1632,6 @@ fn buildOutputType(
1632 };1632 };
1633 defer zig_lib_directory.handle.close();1633 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
1642 var libc_installation: ?LibCInstallation = null;1635 var libc_installation: ?LibCInstallation = null;
1643 defer if (libc_installation) |*l| l.deinit(gpa);1636 defer if (libc_installation) |*l| l.deinit(gpa);
16441637
...@@ -1754,7 +1747,6 @@ fn buildOutputType(...@@ -1754,7 +1747,6 @@ fn buildOutputType(
1754 .single_threaded = single_threaded,1747 .single_threaded = single_threaded,
1755 .function_sections = function_sections,1748 .function_sections = function_sections,
1756 .self_exe_path = self_exe_path,1749 .self_exe_path = self_exe_path,
1757 .rand = &default_prng.random,
1758 .clang_passthrough_mode = arg_mode != .build,1750 .clang_passthrough_mode = arg_mode != .build,
1759 .clang_preprocessor_mode = clang_preprocessor_mode,1751 .clang_preprocessor_mode = clang_preprocessor_mode,
1760 .version = optional_version,1752 .version = optional_version,
...@@ -2420,12 +2412,6 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2420,12 +2412,6 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2420 .directory = null, // Use the local zig-cache.2412 .directory = null, // Use the local zig-cache.
2421 .basename = exe_basename,2413 .basename = exe_basename,
2422 };2414 };
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);
2429 const comp = Compilation.create(gpa, .{2415 const comp = Compilation.create(gpa, .{
2430 .zig_lib_directory = zig_lib_directory,2416 .zig_lib_directory = zig_lib_directory,
2431 .local_cache_directory = local_cache_directory,2417 .local_cache_directory = local_cache_directory,
...@@ -2441,7 +2427,6 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -2441,7 +2427,6 @@ pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
2441 .emit_h = null,2427 .emit_h = null,
2442 .optimize_mode = .Debug,2428 .optimize_mode = .Debug,
2443 .self_exe_path = self_exe_path,2429 .self_exe_path = self_exe_path,
2444 .rand = &default_prng.random,
2445 }) catch |err| {2430 }) catch |err| {
2446 fatal("unable to create compilation: {}", .{@errorName(err)});2431 fatal("unable to create compilation: {}", .{@errorName(err)});
2447 };2432 };
src/musl.zig-1
...@@ -200,7 +200,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -200,7 +200,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
200 .root_pkg = null,200 .root_pkg = null,
201 .output_mode = .Lib,201 .output_mode = .Lib,
202 .link_mode = .Dynamic,202 .link_mode = .Dynamic,
203 .rand = comp.rand,
204 .libc_installation = comp.bin_file.options.libc_installation,203 .libc_installation = comp.bin_file.options.libc_installation,
205 .emit_bin = Compilation.EmitLoc{ .directory = null, .basename = "libc.so" },204 .emit_bin = Compilation.EmitLoc{ .directory = null, .basename = "libc.so" },
206 .optimize_mode = comp.bin_file.options.optimize_mode,205 .optimize_mode = comp.bin_file.options.optimize_mode,
src/test.zig+1-10
...@@ -467,13 +467,6 @@ pub const TestContext = struct {...@@ -467,13 +467,6 @@ pub const TestContext = struct {
467 defer zig_lib_directory.handle.close();467 defer zig_lib_directory.handle.close();
468 defer std.testing.allocator.free(zig_lib_directory.path.?);468 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
477 for (self.cases.items) |case| {470 for (self.cases.items) |case| {
478 if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch)471 if (build_options.skip_non_native and case.target.getCpuArch() != std.Target.current.cpu.arch)
479 continue;472 continue;
...@@ -487,7 +480,7 @@ pub const TestContext = struct {...@@ -487,7 +480,7 @@ pub const TestContext = struct {
487 progress.initial_delay_ns = 0;480 progress.initial_delay_ns = 0;
488 progress.refresh_rate_ns = 0;481 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);
491 }484 }
492 }485 }
493486
...@@ -497,7 +490,6 @@ pub const TestContext = struct {...@@ -497,7 +490,6 @@ pub const TestContext = struct {
497 root_node: *std.Progress.Node,490 root_node: *std.Progress.Node,
498 case: Case,491 case: Case,
499 zig_lib_directory: Compilation.Directory,492 zig_lib_directory: Compilation.Directory,
500 rand: *std.rand.Random,
501 ) !void {493 ) !void {
502 const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);494 const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
503 const target = target_info.target;495 const target = target_info.target;
...@@ -547,7 +539,6 @@ pub const TestContext = struct {...@@ -547,7 +539,6 @@ pub const TestContext = struct {
547 .local_cache_directory = zig_cache_directory,539 .local_cache_directory = zig_cache_directory,
548 .global_cache_directory = zig_cache_directory,540 .global_cache_directory = zig_cache_directory,
549 .zig_lib_directory = zig_lib_directory,541 .zig_lib_directory = zig_lib_directory,
550 .rand = rand,
551 .root_name = "test_case",542 .root_name = "test_case",
552 .target = target,543 .target = target,
553 // TODO: support tests for object file building, and library builds544 // 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 {...@@ -282,10 +282,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
282 \\source.zig:10:8: [address] in main (test)282 \\source.zig:10:8: [address] in main (test)
283 \\ foo();283 \\ foo();
284 \\ ^284 \\ ^
285 \\start.zig:341:29: [address] in std.start.posixCallMainAndExit (test)285 \\start.zig:342:29: [address] in std.start.posixCallMainAndExit (test)
286 \\ return root.main();286 \\ return root.main();
287 \\ ^287 \\ ^
288 \\start.zig:162:5: [address] in std.start._start (test)288 \\start.zig:163:5: [address] in std.start._start (test)
289 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});289 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
290 \\ ^290 \\ ^
291 \\291 \\
...@@ -294,7 +294,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -294,7 +294,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
294 switch (std.Target.current.cpu.arch) {294 switch (std.Target.current.cpu.arch) {
295 .aarch64 => "", // TODO disabled; results in segfault295 .aarch64 => "", // TODO disabled; results in segfault
296 else => 296 else =>
297 \\start.zig:162:5: [address] in std.start._start (test)297 \\start.zig:163:5: [address] in std.start._start (test)
298 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});298 \\ @call(.{ .modifier = .never_inline }, posixCallMainAndExit, .{});
299 \\ ^299 \\ ^
300 \\300 \\
test/standalone/guess_number/main.zig+1-9
...@@ -9,15 +9,7 @@ pub fn main() !void {...@@ -9,15 +9,7 @@ pub fn main() !void {
99
10 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});10 try stdout.print("Welcome to the Guess Number Game in Zig.\n", .{});
1111
12 var seed_bytes: [@sizeOf(u64)]u8 = undefined;12 const answer = std.crypto.random.intRangeLessThan(u8, 0, 100) + 1;
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;
2113
22 while (true) {14 while (true) {
23 try stdout.print("\nGuess a number between 1 and 100: ", .{});15 try stdout.print("\nGuess a number between 1 and 100: ", .{});