aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/Progress.zig345
-rw-r--r--lib/std/auto_reset_event.zig44
-rw-r--r--lib/std/build.zig21
-rw-r--r--lib/std/c.zig14
-rw-r--r--lib/std/c/builtins.zig118
-rw-r--r--lib/std/c/linux.zig6
-rw-r--r--lib/std/crypto.zig11
-rw-r--r--lib/std/crypto/25519/ed25519.zig8
-rw-r--r--lib/std/crypto/25519/edwards25519.zig4
-rw-r--r--lib/std/crypto/25519/x25519.zig2
-rw-r--r--lib/std/crypto/bcrypt.zig4
-rw-r--r--lib/std/crypto/gimli.zig27
-rw-r--r--lib/std/crypto/salsa20.zig18
-rw-r--r--lib/std/crypto/tlcsprng.zig156
-rw-r--r--lib/std/crypto/utils.zig8
-rw-r--r--lib/std/debug.zig21
-rw-r--r--lib/std/fs.zig4
-rw-r--r--lib/std/fs/file.zig3
-rw-r--r--lib/std/heap.zig26
-rw-r--r--lib/std/io.zig11
-rw-r--r--lib/std/io/counting_reader.zig48
-rw-r--r--lib/std/io/serialization.zig616
-rw-r--r--lib/std/macho.zig27
-rw-r--r--lib/std/math.zig25
-rw-r--r--lib/std/meta.zig8
-rw-r--r--lib/std/os.zig112
-rw-r--r--lib/std/os/bits/darwin.zig35
-rw-r--r--lib/std/os/bits/dragonfly.zig14
-rw-r--r--lib/std/os/bits/freebsd.zig104
-rw-r--r--lib/std/os/bits/linux.zig96
-rw-r--r--lib/std/os/bits/linux/arm-eabi.zig3
-rw-r--r--lib/std/os/bits/linux/arm64.zig3
-rw-r--r--lib/std/os/bits/linux/i386.zig5
-rw-r--r--lib/std/os/bits/linux/powerpc64.zig3
-rw-r--r--lib/std/os/bits/linux/riscv64.zig3
-rw-r--r--lib/std/os/bits/linux/sparc64.zig4
-rw-r--r--lib/std/os/bits/linux/x86_64.zig3
-rw-r--r--lib/std/os/bits/netbsd.zig15
-rw-r--r--lib/std/os/bits/openbsd.zig133
-rw-r--r--lib/std/os/linux.zig67
-rw-r--r--lib/std/os/linux/test.zig9
-rw-r--r--lib/std/os/linux/tls.zig37
-rw-r--r--lib/std/os/test.zig38
-rw-r--r--lib/std/progress.zig310
-rw-r--r--lib/std/rand.zig579
-rw-r--r--lib/std/rand/Gimli.zig40
-rw-r--r--lib/std/rand/Isaac64.zig210
-rw-r--r--lib/std/rand/Pcg.zig101
-rw-r--r--lib/std/rand/Sfc64.zig108
-rw-r--r--lib/std/rand/Xoroshiro128.zig133
-rw-r--r--lib/std/special/c.zig10
-rw-r--r--lib/std/special/test_runner.zig2
-rw-r--r--lib/std/start.zig1
-rw-r--r--lib/std/std.zig2
-rw-r--r--lib/std/testing.zig3
55 files changed, 2069 insertions, 1689 deletions
diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig
new file mode 100644
index 0000000000..ae9b1783be
--- /dev/null
+++ b/lib/std/Progress.zig
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! This API non-allocating, non-fallible, and thread-safe.
+//! The tradeoff is that users of this API must provide the storage
+//! for each `Progress.Node`.
+//!
+//! Initialize the struct directly, overriding these fields as desired:
+//! * `refresh_rate_ms`
+//! * `initial_delay_ms`
+
+const std = @import("std");
+const windows = std.os.windows;
+const testing = std.testing;
+const assert = std.debug.assert;
+const Progress = @This();
+
+/// `null` if the current node (and its children) should
+/// not print on update()
+terminal: ?std.fs.File = undefined,
+
+/// Whether the terminal supports ANSI escape codes.
+supports_ansi_escape_codes: bool = false,
+
+root: Node = undefined,
+
+/// Keeps track of how much time has passed since the beginning.
+/// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.
+timer: std.time.Timer = undefined,
+
+/// When the previous refresh was written to the terminal.
+/// Used to compare with `refresh_rate_ms`.
+prev_refresh_timestamp: u64 = undefined,
+
+/// This buffer represents the maximum number of bytes written to the terminal
+/// with each refresh.
+output_buffer: [100]u8 = undefined,
+
+/// How many nanoseconds between writing updates to the terminal.
+refresh_rate_ns: u64 = 50 * std.time.ns_per_ms,
+
+/// How many nanoseconds to keep the output hidden
+initial_delay_ns: u64 = 500 * std.time.ns_per_ms,
+
+done: bool = true,
+
+/// Protects the `refresh` function, as well as `node.recently_updated_child`.
+/// Without this, callsites would call `Node.end` and then free `Node` memory
+/// while it was still being accessed by the `refresh` function.
+update_lock: std.Mutex = .{},
+
+/// Keeps track of how many columns in the terminal have been output, so that
+/// we can move the cursor back later.
+columns_written: usize = undefined,
+
+/// Represents one unit of progress. Each node can have children nodes, or
+/// one can use integers with `update`.
+pub const Node = struct {
+ context: *Progress,
+ parent: ?*Node,
+ name: []const u8,
+ /// Must be handled atomically to be thread-safe.
+ recently_updated_child: ?*Node = null,
+ /// Must be handled atomically to be thread-safe. 0 means null.
+ unprotected_estimated_total_items: usize,
+ /// Must be handled atomically to be thread-safe.
+ unprotected_completed_items: usize,
+
+ /// Create a new child progress node. Thread-safe.
+ /// Call `Node.end` when done.
+ /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
+ /// API to set `self.parent.recently_updated_child` with the return value.
+ /// Until that is fixed you probably want to call `activate` on the return value.
+ /// Passing 0 for `estimated_total_items` means unknown.
+ pub fn start(self: *Node, name: []const u8, estimated_total_items: usize) Node {
+ return Node{
+ .context = self.context,
+ .parent = self,
+ .name = name,
+ .unprotected_estimated_total_items = estimated_total_items,
+ .unprotected_completed_items = 0,
+ };
+ }
+
+ /// This is the same as calling `start` and then `end` on the returned `Node`. Thread-safe.
+ pub fn completeOne(self: *Node) void {
+ self.activate();
+ _ = @atomicRmw(usize, &self.unprotected_completed_items, .Add, 1, .Monotonic);
+ self.context.maybeRefresh();
+ }
+
+ /// Finish a started `Node`. Thread-safe.
+ pub fn end(self: *Node) void {
+ self.context.maybeRefresh();
+ if (self.parent) |parent| {
+ {
+ const held = self.context.update_lock.acquire();
+ defer held.release();
+ _ = @cmpxchgStrong(?*Node, &parent.recently_updated_child, self, null, .Monotonic, .Monotonic);
+ }
+ parent.completeOne();
+ } else {
+ self.context.done = true;
+ self.context.refresh();
+ }
+ }
+
+ /// Tell the parent node that this node is actively being worked on. Thread-safe.
+ pub fn activate(self: *Node) void {
+ if (self.parent) |parent| {
+ @atomicStore(?*Node, &parent.recently_updated_child, self, .Release);
+ }
+ }
+
+ /// Thread-safe. 0 means unknown.
+ pub fn setEstimatedTotalItems(self: *Node, count: usize) void {
+ @atomicStore(usize, &self.unprotected_estimated_total_items, count, .Monotonic);
+ }
+
+ /// Thread-safe.
+ pub fn setCompletedItems(self: *Node, completed_items: usize) void {
+ @atomicStore(usize, &self.unprotected_completed_items, completed_items, .Monotonic);
+ }
+};
+
+/// Create a new progress node.
+/// Call `Node.end` when done.
+/// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
+/// API to return Progress rather than accept it as a parameter.
+/// `estimated_total_items` value of 0 means unknown.
+pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !*Node {
+ const stderr = std.io.getStdErr();
+ self.terminal = null;
+ if (stderr.supportsAnsiEscapeCodes()) {
+ self.terminal = stderr;
+ self.supports_ansi_escape_codes = true;
+ } else if (std.builtin.os.tag == .windows and stderr.isTty()) {
+ self.terminal = stderr;
+ }
+ self.root = Node{
+ .context = self,
+ .parent = null,
+ .name = name,
+ .unprotected_estimated_total_items = estimated_total_items,
+ .unprotected_completed_items = 0,
+ };
+ self.columns_written = 0;
+ self.prev_refresh_timestamp = 0;
+ self.timer = try std.time.Timer.start();
+ self.done = false;
+ return &self.root;
+}
+
+/// Updates the terminal if enough time has passed since last update. Thread-safe.
+pub fn maybeRefresh(self: *Progress) void {
+ const now = self.timer.read();
+ if (now < self.initial_delay_ns) return;
+ const held = self.update_lock.tryAcquire() orelse return;
+ defer held.release();
+ if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
+ return self.refreshWithHeldLock();
+}
+
+/// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe.
+pub fn refresh(self: *Progress) void {
+ const held = self.update_lock.tryAcquire() orelse return;
+ defer held.release();
+
+ return self.refreshWithHeldLock();
+}
+
+fn refreshWithHeldLock(self: *Progress) void {
+ const file = self.terminal orelse return;
+
+ const prev_columns_written = self.columns_written;
+ var end: usize = 0;
+ if (self.columns_written > 0) {
+ // restore the cursor position by moving the cursor
+ // `columns_written` cells to the left, then clear the rest of the
+ // line
+ if (self.supports_ansi_escape_codes) {
+ end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{d}D", .{self.columns_written}) catch unreachable).len;
+ end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
+ } else if (std.builtin.os.tag == .windows) winapi: {
+ var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
+ if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
+ unreachable;
+
+ var cursor_pos = windows.COORD{
+ .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
+ .Y = info.dwCursorPosition.Y,
+ };
+
+ if (cursor_pos.X < 0)
+ cursor_pos.X = 0;
+
+ const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
+
+ var written: windows.DWORD = undefined;
+ if (windows.kernel32.FillConsoleOutputAttribute(
+ file.handle,
+ info.wAttributes,
+ fill_chars,
+ cursor_pos,
+ &written,
+ ) != windows.TRUE) {
+ // Stop trying to write to this file.
+ self.terminal = null;
+ break :winapi;
+ }
+ if (windows.kernel32.FillConsoleOutputCharacterA(
+ file.handle,
+ ' ',
+ fill_chars,
+ cursor_pos,
+ &written,
+ ) != windows.TRUE) unreachable;
+
+ if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
+ unreachable;
+ } else unreachable;
+
+ self.columns_written = 0;
+ }
+
+ if (!self.done) {
+ var need_ellipse = false;
+ var maybe_node: ?*Node = &self.root;
+ while (maybe_node) |node| {
+ if (need_ellipse) {
+ self.bufWrite(&end, "... ", .{});
+ }
+ need_ellipse = false;
+ const eti = @atomicLoad(usize, &node.unprotected_estimated_total_items, .Monotonic);
+ const completed_items = @atomicLoad(usize, &node.unprotected_completed_items, .Monotonic);
+ if (node.name.len != 0 or eti > 0) {
+ if (node.name.len != 0) {
+ self.bufWrite(&end, "{s}", .{node.name});
+ need_ellipse = true;
+ }
+ if (eti > 0) {
+ if (need_ellipse) self.bufWrite(&end, " ", .{});
+ self.bufWrite(&end, "[{d}/{d}] ", .{ completed_items + 1, eti });
+ need_ellipse = false;
+ } else if (completed_items != 0) {
+ if (need_ellipse) self.bufWrite(&end, " ", .{});
+ self.bufWrite(&end, "[{d}] ", .{completed_items + 1});
+ need_ellipse = false;
+ }
+ }
+ maybe_node = @atomicLoad(?*Node, &node.recently_updated_child, .Acquire);
+ }
+ if (need_ellipse) {
+ self.bufWrite(&end, "... ", .{});
+ }
+ }
+
+ _ = file.write(self.output_buffer[0..end]) catch |e| {
+ // Stop trying to write to this file once it errors.
+ self.terminal = null;
+ };
+ self.prev_refresh_timestamp = self.timer.read();
+}
+
+pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
+ const file = self.terminal orelse return;
+ self.refresh();
+ file.outStream().print(format, args) catch {
+ self.terminal = null;
+ return;
+ };
+ self.columns_written = 0;
+}
+
+fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
+ if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
+ const amt = written.len;
+ end.* += amt;
+ self.columns_written += amt;
+ } else |err| switch (err) {
+ error.NoSpaceLeft => {
+ self.columns_written += self.output_buffer.len - end.*;
+ end.* = self.output_buffer.len;
+ },
+ }
+ const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;
+ const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
+ if (end.* > max_end) {
+ const suffix = "... ";
+ self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
+ std.mem.copy(u8, self.output_buffer[max_end..], suffix);
+ end.* = max_end + suffix.len;
+ }
+}
+
+test "basic functionality" {
+ var disable = true;
+ if (disable) {
+ // This test is disabled because it uses time.sleep() and is therefore slow. It also
+ // prints bogus progress data to stderr.
+ return error.SkipZigTest;
+ }
+ var progress = Progress{};
+ const root_node = try progress.start("", 100);
+ defer root_node.end();
+
+ const sub_task_names = [_][]const u8{
+ "reticulating splines",
+ "adjusting shoes",
+ "climbing towers",
+ "pouring juice",
+ };
+ var next_sub_task: usize = 0;
+
+ var i: usize = 0;
+ while (i < 100) : (i += 1) {
+ var node = root_node.start(sub_task_names[next_sub_task], 5);
+ node.activate();
+ next_sub_task = (next_sub_task + 1) % sub_task_names.len;
+
+ node.completeOne();
+ std.time.sleep(5 * std.time.ns_per_ms);
+ node.completeOne();
+ node.completeOne();
+ std.time.sleep(5 * std.time.ns_per_ms);
+ node.completeOne();
+ node.completeOne();
+ std.time.sleep(5 * std.time.ns_per_ms);
+
+ node.end();
+
+ std.time.sleep(5 * std.time.ns_per_ms);
+ }
+ {
+ var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", 0);
+ node.activate();
+ std.time.sleep(10 * std.time.ns_per_ms);
+ progress.refresh();
+ std.time.sleep(10 * std.time.ns_per_ms);
+ node.end();
+ }
+}
diff --git a/lib/std/auto_reset_event.zig b/lib/std/auto_reset_event.zig
index 7e13dc1aba..3c7e65e362 100644
--- a/lib/std/auto_reset_event.zig
+++ b/lib/std/auto_reset_event.zig
@@ -11,33 +11,33 @@ const assert = std.debug.assert;
/// Similar to std.ResetEvent but on `set()` it also (atomically) does `reset()`.
/// Unlike std.ResetEvent, `wait()` can only be called by one thread (MPSC-like).
pub const AutoResetEvent = struct {
- // AutoResetEvent has 3 possible states:
- // - UNSET: the AutoResetEvent is currently unset
- // - SET: the AutoResetEvent was notified before a wait() was called
- // - <std.ResetEvent pointer>: there is an active waiter waiting for a notification.
- //
- // When attempting to wait:
- // if the event is unset, it registers a ResetEvent pointer to be notified when the event is set
- // if the event is already set, then it consumes the notification and resets the event.
- //
- // When attempting to notify:
- // if the event is unset, then we set the event
- // if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent
- //
- // This ensures that the event is automatically reset after a wait() has been issued
- // and avoids the race condition when using std.ResetEvent in the following scenario:
- // thread 1 | thread 2
- // std.ResetEvent.wait() |
- // | std.ResetEvent.set()
- // | std.ResetEvent.set()
- // std.ResetEvent.reset() |
- // std.ResetEvent.wait() | (missed the second .set() notification above)
+ /// AutoResetEvent has 3 possible states:
+ /// - UNSET: the AutoResetEvent is currently unset
+ /// - SET: the AutoResetEvent was notified before a wait() was called
+ /// - <std.ResetEvent pointer>: there is an active waiter waiting for a notification.
+ ///
+ /// When attempting to wait:
+ /// if the event is unset, it registers a ResetEvent pointer to be notified when the event is set
+ /// if the event is already set, then it consumes the notification and resets the event.
+ ///
+ /// When attempting to notify:
+ /// if the event is unset, then we set the event
+ /// if theres a waiting ResetEvent, then we unset the event and notify the ResetEvent
+ ///
+ /// This ensures that the event is automatically reset after a wait() has been issued
+ /// and avoids the race condition when using std.ResetEvent in the following scenario:
+ /// thread 1 | thread 2
+ /// std.ResetEvent.wait() |
+ /// | std.ResetEvent.set()
+ /// | std.ResetEvent.set()
+ /// std.ResetEvent.reset() |
+ /// std.ResetEvent.wait() | (missed the second .set() notification above)
state: usize = UNSET,
const UNSET = 0;
const SET = 1;
- // the minimum alignment for the `*std.ResetEvent` created by wait*()
+ /// the minimum alignment for the `*std.ResetEvent` created by wait*()
const event_align = std.math.max(@alignOf(std.ResetEvent), 2);
pub fn wait(self: *AutoResetEvent) void {
diff --git a/lib/std/build.zig b/lib/std/build.zig
index dacfaf5f75..8d6db459ea 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -509,7 +509,26 @@ pub const Builder = struct {
return null;
},
},
- .Float => panic("TODO float options to build script", .{}),
+ .Float => switch (entry.value.value) {
+ .Flag => {
+ warn("Expected -D{} to be a float, but received a boolean.\n", .{name});
+ self.markInvalidUserInput();
+ return null;
+ },
+ .Scalar => |s| {
+ const n = std.fmt.parseFloat(T, s) catch |err| {
+ warn("Expected -D{} to be a float of type {}.\n", .{ name, @typeName(T) });
+ self.markInvalidUserInput();
+ return null;
+ };
+ return n;
+ },
+ .List => {
+ warn("Expected -D{} to be a float, but received a list.\n", .{name});
+ self.markInvalidUserInput();
+ return null;
+ },
+ },
.Enum => switch (entry.value.value) {
.Flag => {
warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
diff --git a/lib/std/c.zig b/lib/std/c.zig
index 5ebbb9dd22..aae3f383d1 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -12,6 +12,7 @@ pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
pub const parse = @import("c/parse.zig").parse;
pub const ast = @import("c/ast.zig");
+pub const builtins = @import("c/builtins.zig");
test "" {
_ = tokenizer;
@@ -200,7 +201,7 @@ pub usingnamespace switch (builtin.os.tag) {
pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
pub extern "c" fn sched_yield() c_int;
- pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int;
+ pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *libc_stat) c_int;
@@ -215,7 +216,7 @@ pub usingnamespace switch (builtin.os.tag) {
pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
pub extern "c" fn sched_yield() c_int;
- pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int;
+ pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *libc_stat) c_int;
},
@@ -227,7 +228,7 @@ pub usingnamespace switch (builtin.os.tag) {
pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
pub extern "c" fn sched_yield() c_int;
- pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int;
+ pub extern "c" fn sigaction(sig: c_int, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) c_int;
pub extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int;
pub extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int;
pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *libc_stat) c_int;
@@ -264,6 +265,11 @@ pub extern "c" fn pthread_attr_setguardsize(attr: *pthread_attr_t, guardsize: us
pub extern "c" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
pub extern "c" fn pthread_self() pthread_t;
pub extern "c" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;
+pub extern "c" fn pthread_atfork(
+ prepare: ?fn () callconv(.C) void,
+ parent: ?fn () callconv(.C) void,
+ child: ?fn () callconv(.C) void,
+) c_int;
pub extern "c" fn kqueue() c_int;
pub extern "c" fn kevent(
@@ -336,6 +342,8 @@ pub extern "c" fn prctl(option: c_int, ...) c_int;
pub extern "c" fn getrlimit(resource: rlimit_resource, rlim: *rlimit) c_int;
pub extern "c" fn setrlimit(resource: rlimit_resource, rlim: *const rlimit) c_int;
+pub extern "c" fn fmemopen(noalias buf: ?*c_void, size: usize, noalias mode: [*:0]const u8) ?*FILE;
+
pub const max_align_t = if (std.Target.current.abi == .msvc)
f64
else if (std.Target.current.isDarwin())
diff --git a/lib/std/c/builtins.zig b/lib/std/c/builtins.zig
new file mode 100644
index 0000000000..3dd6e23b91
--- /dev/null
+++ b/lib/std/c/builtins.zig
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+const std = @import("std");
+
+pub inline fn __builtin_bswap16(val: u16) callconv(.C) u16 { return @byteSwap(u16, val); }
+pub inline fn __builtin_bswap32(val: u32) callconv(.C) u32 { return @byteSwap(u32, val); }
+pub inline fn __builtin_bswap64(val: u64) callconv(.C) u64 { return @byteSwap(u64, val); }
+
+pub inline fn __builtin_signbit(val: f64) callconv(.C) c_int { return @boolToInt(std.math.signbit(val)); }
+pub inline fn __builtin_signbitf(val: f32) callconv(.C) c_int { return @boolToInt(std.math.signbit(val)); }
+
+pub inline fn __builtin_popcount(val: c_uint) callconv(.C) c_int {
+ // popcount of a c_uint will never exceed the capacity of a c_int
+ @setRuntimeSafety(false);
+ return @bitCast(c_int, @as(c_uint, @popCount(c_uint, val)));
+}
+pub inline fn __builtin_ctz(val: c_uint) callconv(.C) c_int {
+ // Returns the number of trailing 0-bits in val, starting at the least significant bit position.
+ // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
+ @setRuntimeSafety(false);
+ return @bitCast(c_int, @as(c_uint, @ctz(c_uint, val)));
+}
+pub inline fn __builtin_clz(val: c_uint) callconv(.C) c_int {
+ // Returns the number of leading 0-bits in x, starting at the most significant bit position.
+ // In C if `val` is 0, the result is undefined; in zig it's the number of bits in a c_uint
+ @setRuntimeSafety(false);
+ return @bitCast(c_int, @as(c_uint, @clz(c_uint, val)));
+}
+
+pub inline fn __builtin_sqrt(val: f64) callconv(.C) f64 { return @sqrt(val); }
+pub inline fn __builtin_sqrtf(val: f32) callconv(.C) f32 { return @sqrt(val); }
+
+pub inline fn __builtin_sin(val: f64) callconv(.C) f64 { return @sin(val); }
+pub inline fn __builtin_sinf(val: f32) callconv(.C) f32 { return @sin(val); }
+pub inline fn __builtin_cos(val: f64) callconv(.C) f64 { return @cos(val); }
+pub inline fn __builtin_cosf(val: f32) callconv(.C) f32 { return @cos(val); }
+
+pub inline fn __builtin_exp(val: f64) callconv(.C) f64 { return @exp(val); }
+pub inline fn __builtin_expf(val: f32) callconv(.C) f32 { return @exp(val); }
+pub inline fn __builtin_exp2(val: f64) callconv(.C) f64 { return @exp2(val); }
+pub inline fn __builtin_exp2f(val: f32) callconv(.C) f32 { return @exp2(val); }
+pub inline fn __builtin_log(val: f64) callconv(.C) f64 { return @log(val); }
+pub inline fn __builtin_logf(val: f32) callconv(.C) f32 { return @log(val); }
+pub inline fn __builtin_log2(val: f64) callconv(.C) f64 { return @log2(val); }
+pub inline fn __builtin_log2f(val: f32) callconv(.C) f32 { return @log2(val); }
+pub inline fn __builtin_log10(val: f64) callconv(.C) f64 { return @log10(val); }
+pub inline fn __builtin_log10f(val: f32) callconv(.C) f32 { return @log10(val); }
+
+// Standard C Library bug: The absolute value of the most negative integer remains negative.
+pub inline fn __builtin_abs(val: c_int) callconv(.C) c_int { return std.math.absInt(val) catch std.math.minInt(c_int); }
+pub inline fn __builtin_fabs(val: f64) callconv(.C) f64 { return @fabs(val); }
+pub inline fn __builtin_fabsf(val: f32) callconv(.C) f32 { return @fabs(val); }
+
+pub inline fn __builtin_floor(val: f64) callconv(.C) f64 { return @floor(val); }
+pub inline fn __builtin_floorf(val: f32) callconv(.C) f32 { return @floor(val); }
+pub inline fn __builtin_ceil(val: f64) callconv(.C) f64 { return @ceil(val); }
+pub inline fn __builtin_ceilf(val: f32) callconv(.C) f32 { return @ceil(val); }
+pub inline fn __builtin_trunc(val: f64) callconv(.C) f64 { return @trunc(val); }
+pub inline fn __builtin_truncf(val: f32) callconv(.C) f32 { return @trunc(val); }
+pub inline fn __builtin_round(val: f64) callconv(.C) f64 { return @round(val); }
+pub inline fn __builtin_roundf(val: f32) callconv(.C) f32 { return @round(val); }
+
+pub inline fn __builtin_strlen(s: [*c]const u8) callconv(.C) usize { return std.mem.lenZ(s); }
+pub inline fn __builtin_strcmp(s1: [*c]const u8, s2: [*c]const u8) callconv(.C) c_int {
+ return @as(c_int, std.cstr.cmp(s1, s2));
+}
+
+pub inline fn __builtin_object_size(ptr: ?*const c_void, ty: c_int) callconv(.C) usize {
+ // clang semantics match gcc's: https://gcc.gnu.org/onlinedocs/gcc/Object-Size-Checking.html
+ // If it is not possible to determine which objects ptr points to at compile time,
+ // __builtin_object_size should return (size_t) -1 for type 0 or 1 and (size_t) 0
+ // for type 2 or 3.
+ if (ty == 0 or ty == 1) return @bitCast(usize, -@as(c_long, 1));
+ if (ty == 2 or ty == 3) return 0;
+ unreachable;
+}
+
+pub inline fn __builtin___memset_chk(
+ dst: ?*c_void,
+ val: c_int,
+ len: usize,
+ remaining: usize,
+) callconv(.C) ?*c_void {
+ if (len > remaining) @panic("std.c.builtins.memset_chk called with len > remaining");
+ return __builtin_memset(dst, val, len);
+}
+
+pub inline fn __builtin_memset(dst: ?*c_void, val: c_int, len: usize) callconv(.C) ?*c_void {
+ const dst_cast = @ptrCast([*c]u8, dst);
+ @memset(dst_cast, @bitCast(u8, @truncate(i8, val)), len);
+ return dst;
+}
+
+pub inline fn __builtin___memcpy_chk(
+ noalias dst: ?*c_void,
+ noalias src: ?*const c_void,
+ len: usize,
+ remaining: usize,
+) callconv(.C) ?*c_void {
+ if (len > remaining) @panic("std.c.builtins.memcpy_chk called with len > remaining");
+ return __builtin_memcpy(dst, src, len);
+}
+
+pub inline fn __builtin_memcpy(
+ noalias dst: ?*c_void,
+ noalias src: ?*const c_void,
+ len: usize,
+) callconv(.C) ?*c_void {
+ const dst_cast = @ptrCast([*c]u8, dst);
+ const src_cast = @ptrCast([*c]const u8, src);
+
+ @memcpy(dst_cast, src_cast, len);
+ return dst;
+}
diff --git a/lib/std/c/linux.zig b/lib/std/c/linux.zig
index 21124d1030..97a25617ef 100644
--- a/lib/std/c/linux.zig
+++ b/lib/std/c/linux.zig
@@ -106,6 +106,12 @@ pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *con
pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
pub extern "c" fn malloc_usable_size(?*const c_void) usize;
+pub extern "c" fn madvise(
+ addr: *align(std.mem.page_size) c_void,
+ length: usize,
+ advice: c_uint,
+) c_int;
+
pub const pthread_attr_t = extern struct {
__size: [56]u8,
__align: c_long,
diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig
index 6eb934473f..e3581cde96 100644
--- a/lib/std/crypto.zig
+++ b/lib/std/crypto.zig
@@ -134,8 +134,10 @@ pub const nacl = struct {
pub const utils = @import("crypto/utils.zig");
+/// This is a thread-local, cryptographically secure pseudo random number generator.
+pub const random = &@import("crypto/tlcsprng.zig").interface;
+
const std = @import("std.zig");
-pub const randomBytes = std.os.getrandom;
test "crypto" {
inline for (std.meta.declarations(@This())) |decl| {
@@ -178,6 +180,13 @@ test "crypto" {
_ = @import("crypto/25519/ristretto255.zig");
}
+test "CSPRNG" {
+ const a = random.int(u64);
+ const b = random.int(u64);
+ const c = random.int(u64);
+ std.testing.expect(a ^ b ^ c != 0);
+}
+
test "issue #4532: no index out of bounds" {
const types = [_]type{
hash.Md5,
diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig
index 842b08d706..7f90ba584c 100644
--- a/lib/std/crypto/25519/ed25519.zig
+++ b/lib/std/crypto/25519/ed25519.zig
@@ -43,7 +43,7 @@ pub const Ed25519 = struct {
pub fn create(seed: ?[seed_length]u8) !KeyPair {
const ss = seed orelse ss: {
var random_seed: [seed_length]u8 = undefined;
- try crypto.randomBytes(&random_seed);
+ crypto.random.bytes(&random_seed);
break :ss random_seed;
};
var az: [Sha512.digest_length]u8 = undefined;
@@ -179,7 +179,7 @@ pub const Ed25519 = struct {
var z_batch: [count]Curve.scalar.CompressedScalar = undefined;
for (z_batch) |*z| {
- try std.crypto.randomBytes(z[0..16]);
+ std.crypto.random.bytes(z[0..16]);
mem.set(u8, z[16..], 0);
}
@@ -232,8 +232,8 @@ test "ed25519 batch verification" {
const key_pair = try Ed25519.KeyPair.create(null);
var msg1: [32]u8 = undefined;
var msg2: [32]u8 = undefined;
- try std.crypto.randomBytes(&msg1);
- try std.crypto.randomBytes(&msg2);
+ std.crypto.random.bytes(&msg1);
+ std.crypto.random.bytes(&msg2);
const sig1 = try Ed25519.sign(&msg1, key_pair, null);
const sig2 = try Ed25519.sign(&msg2, key_pair, null);
var signature_batch = [_]Ed25519.BatchElement{
diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig
index 008a4535b3..d64f06c421 100644
--- a/lib/std/crypto/25519/edwards25519.zig
+++ b/lib/std/crypto/25519/edwards25519.zig
@@ -484,8 +484,8 @@ test "edwards25519 packing/unpacking" {
test "edwards25519 point addition/substraction" {
var s1: [32]u8 = undefined;
var s2: [32]u8 = undefined;
- try std.crypto.randomBytes(&s1);
- try std.crypto.randomBytes(&s2);
+ std.crypto.random.bytes(&s1);
+ std.crypto.random.bytes(&s2);
const p = try Edwards25519.basePoint.clampedMul(s1);
const q = try Edwards25519.basePoint.clampedMul(s2);
const r = p.add(q).add(q).sub(q).sub(q);
diff --git a/lib/std/crypto/25519/x25519.zig b/lib/std/crypto/25519/x25519.zig
index 3b3ff551fe..0bf55d52fc 100644
--- a/lib/std/crypto/25519/x25519.zig
+++ b/lib/std/crypto/25519/x25519.zig
@@ -34,7 +34,7 @@ pub const X25519 = struct {
pub fn create(seed: ?[seed_length]u8) !KeyPair {
const sk = seed orelse sk: {
var random_seed: [seed_length]u8 = undefined;
- try crypto.randomBytes(&random_seed);
+ crypto.random.bytes(&random_seed);
break :sk random_seed;
};
var kp: KeyPair = undefined;
diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig
index 4cec59961b..6813495d25 100644
--- a/lib/std/crypto/bcrypt.zig
+++ b/lib/std/crypto/bcrypt.zig
@@ -262,7 +262,7 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8)
/// and then use the resulting hash as the password parameter for bcrypt.
pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 {
var salt: [salt_length]u8 = undefined;
- try crypto.randomBytes(&salt);
+ crypto.random.bytes(&salt);
return strHashInternal(password, rounds_log, salt);
}
@@ -283,7 +283,7 @@ pub fn strVerify(h: [hash_length]u8, password: []const u8) BcryptError!void {
test "bcrypt codec" {
var salt: [salt_length]u8 = undefined;
- try crypto.randomBytes(&salt);
+ crypto.random.bytes(&salt);
var salt_str: [salt_str_length]u8 = undefined;
Codec.encode(salt_str[0..], salt[0..]);
var salt2: [salt_length]u8 = undefined;
diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig
index 78ab88b9cf..f21bc1008a 100644
--- a/lib/std/crypto/gimli.zig
+++ b/lib/std/crypto/gimli.zig
@@ -229,18 +229,17 @@ pub const Hash = struct {
const buf = self.state.toSlice();
var in = data;
while (in.len > 0) {
- var left = State.RATE - self.buf_off;
- if (left == 0) {
- self.state.permute();
- self.buf_off = 0;
- left = State.RATE;
- }
+ const left = State.RATE - self.buf_off;
const ps = math.min(in.len, left);
for (buf[self.buf_off .. self.buf_off + ps]) |*p, i| {
p.* ^= in[i];
}
self.buf_off += ps;
in = in[ps..];
+ if (self.buf_off == State.RATE) {
+ self.state.permute();
+ self.buf_off = 0;
+ }
}
}
@@ -277,6 +276,22 @@ test "hash" {
htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", &md);
}
+test "hash test vector 17" {
+ var msg: [32 / 2]u8 = undefined;
+ try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F");
+ var md: [32]u8 = undefined;
+ hash(&md, &msg, .{});
+ htest.assertEqual("404C130AF1B9023A7908200919F690FFBB756D5176E056FFDE320016A37C7282", &md);
+}
+
+test "hash test vector 33" {
+ var msg: [32]u8 = undefined;
+ try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+ var md: [32]u8 = undefined;
+ hash(&md, &msg, .{});
+ htest.assertEqual("A8F4FA28708BDA7EFB4C1914CA4AFA9E475B82D588D36504F87DBB0ED9AB3C4B", &md);
+}
+
pub const Aead = struct {
pub const tag_length = State.RATE;
pub const nonce_length = 16;
diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig
index dd3e4fe99b..8122e9b25c 100644
--- a/lib/std/crypto/salsa20.zig
+++ b/lib/std/crypto/salsa20.zig
@@ -571,9 +571,9 @@ test "xsalsa20poly1305" {
var key: [XSalsa20Poly1305.key_length]u8 = undefined;
var nonce: [XSalsa20Poly1305.nonce_length]u8 = undefined;
var tag: [XSalsa20Poly1305.tag_length]u8 = undefined;
- try crypto.randomBytes(&msg);
- try crypto.randomBytes(&key);
- try crypto.randomBytes(&nonce);
+ crypto.random.bytes(&msg);
+ crypto.random.bytes(&key);
+ crypto.random.bytes(&nonce);
XSalsa20Poly1305.encrypt(c[0..], &tag, msg[0..], "ad", nonce, key);
try XSalsa20Poly1305.decrypt(msg2[0..], c[0..], tag, "ad", nonce, key);
@@ -585,9 +585,9 @@ test "xsalsa20poly1305 secretbox" {
var key: [XSalsa20Poly1305.key_length]u8 = undefined;
var nonce: [Box.nonce_length]u8 = undefined;
var boxed: [msg.len + Box.tag_length]u8 = undefined;
- try crypto.randomBytes(&msg);
- try crypto.randomBytes(&key);
- try crypto.randomBytes(&nonce);
+ crypto.random.bytes(&msg);
+ crypto.random.bytes(&key);
+ crypto.random.bytes(&nonce);
SecretBox.seal(boxed[0..], msg[0..], nonce, key);
try SecretBox.open(msg2[0..], boxed[0..], nonce, key);
@@ -598,8 +598,8 @@ test "xsalsa20poly1305 box" {
var msg2: [msg.len]u8 = undefined;
var nonce: [Box.nonce_length]u8 = undefined;
var boxed: [msg.len + Box.tag_length]u8 = undefined;
- try crypto.randomBytes(&msg);
- try crypto.randomBytes(&nonce);
+ crypto.random.bytes(&msg);
+ crypto.random.bytes(&nonce);
var kp1 = try Box.KeyPair.create(null);
var kp2 = try Box.KeyPair.create(null);
@@ -611,7 +611,7 @@ test "xsalsa20poly1305 sealedbox" {
var msg: [100]u8 = undefined;
var msg2: [msg.len]u8 = undefined;
var boxed: [msg.len + SealedBox.seal_length]u8 = undefined;
- try crypto.randomBytes(&msg);
+ crypto.random.bytes(&msg);
var kp = try Box.KeyPair.create(null);
try SealedBox.seal(boxed[0..], msg[0..], kp.public_key);
diff --git a/lib/std/crypto/tlcsprng.zig b/lib/std/crypto/tlcsprng.zig
new file mode 100644
index 0000000000..384216a81b
--- /dev/null
+++ b/lib/std/crypto/tlcsprng.zig
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Thread-local cryptographically secure pseudo-random number generator.
+//! This file has public declarations that are intended to be used internally
+//! by the standard library; this namespace is not intended to be exposed
+//! directly to standard library users.
+
+const std = @import("std");
+const root = @import("root");
+const mem = std.mem;
+
+/// We use this as a layer of indirection because global const pointers cannot
+/// point to thread-local variables.
+pub var interface = std.rand.Random{ .fillFn = tlsCsprngFill };
+
+const os_has_fork = switch (std.Target.current.os.tag) {
+ .dragonfly,
+ .freebsd,
+ .ios,
+ .kfreebsd,
+ .linux,
+ .macos,
+ .netbsd,
+ .openbsd,
+ .solaris,
+ .tvos,
+ .watchos,
+ => true,
+
+ else => false,
+};
+const os_has_arc4random = std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf");
+const want_fork_safety = os_has_fork and !os_has_arc4random and
+ (std.meta.globalOption("crypto_fork_safety", bool) orelse true);
+const maybe_have_wipe_on_fork = std.Target.current.os.isAtLeast(.linux, .{
+ .major = 4,
+ .minor = 14,
+}) orelse true;
+
+const WipeMe = struct {
+ init_state: enum { uninitialized, initialized, failed },
+ gimli: std.crypto.core.Gimli,
+};
+const wipe_align = if (maybe_have_wipe_on_fork) mem.page_size else @alignOf(WipeMe);
+
+threadlocal var wipe_me: WipeMe align(wipe_align) = .{
+ .gimli = undefined,
+ .init_state = .uninitialized,
+};
+
+fn tlsCsprngFill(_: *const std.rand.Random, buffer: []u8) void {
+ if (std.builtin.link_libc and @hasDecl(std.c, "arc4random_buf")) {
+ // arc4random is already a thread-local CSPRNG.
+ return std.c.arc4random_buf(buffer.ptr, buffer.len);
+ }
+ // Allow applications to decide they would prefer to have every call to
+ // std.crypto.random always make an OS syscall, rather than rely on an
+ // application implementation of a CSPRNG.
+ if (comptime std.meta.globalOption("crypto_always_getrandom", bool) orelse false) {
+ return fillWithOsEntropy(buffer);
+ }
+ switch (wipe_me.init_state) {
+ .uninitialized => {
+ if (want_fork_safety) {
+ if (maybe_have_wipe_on_fork) {
+ if (std.os.madvise(
+ @ptrCast([*]align(mem.page_size) u8, &wipe_me),
+ @sizeOf(@TypeOf(wipe_me)),
+ std.os.MADV_WIPEONFORK,
+ )) |_| {
+ return initAndFill(buffer);
+ } else |_| if (std.Thread.use_pthreads) {
+ return setupPthreadAtforkAndFill(buffer);
+ } else {
+ // Since we failed to set up fork safety, we fall back to always
+ // calling getrandom every time.
+ wipe_me.init_state = .failed;
+ return fillWithOsEntropy(buffer);
+ }
+ } else if (std.Thread.use_pthreads) {
+ return setupPthreadAtforkAndFill(buffer);
+ } else {
+ // We have no mechanism to provide fork safety, but we want fork safety,
+ // so we fall back to calling getrandom every time.
+ wipe_me.init_state = .failed;
+ return fillWithOsEntropy(buffer);
+ }
+ } else {
+ return initAndFill(buffer);
+ }
+ },
+ .initialized => {
+ return fillWithCsprng(buffer);
+ },
+ .failed => {
+ if (want_fork_safety) {
+ return fillWithOsEntropy(buffer);
+ } else {
+ unreachable;
+ }
+ },
+ }
+}
+
+fn setupPthreadAtforkAndFill(buffer: []u8) void {
+ const failed = std.c.pthread_atfork(null, null, childAtForkHandler) != 0;
+ if (failed) {
+ wipe_me.init_state = .failed;
+ return fillWithOsEntropy(buffer);
+ } else {
+ return initAndFill(buffer);
+ }
+}
+
+fn childAtForkHandler() callconv(.C) void {
+ const wipe_slice = @ptrCast([*]u8, &wipe_me)[0..@sizeOf(@TypeOf(wipe_me))];
+ std.crypto.utils.secureZero(u8, wipe_slice);
+}
+
+fn fillWithCsprng(buffer: []u8) void {
+ if (buffer.len != 0) {
+ wipe_me.gimli.squeeze(buffer);
+ } else {
+ wipe_me.gimli.permute();
+ }
+ mem.set(u8, wipe_me.gimli.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
+}
+
+fn fillWithOsEntropy(buffer: []u8) void {
+ std.os.getrandom(buffer) catch @panic("getrandom() failed to provide entropy");
+}
+
+fn initAndFill(buffer: []u8) void {
+ var seed: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
+ // Because we panic on getrandom() failing, we provide the opportunity
+ // to override the default seed function. This also makes
+ // `std.crypto.random` available on freestanding targets, provided that
+ // the `cryptoRandomSeed` function is provided.
+ if (@hasDecl(root, "cryptoRandomSeed")) {
+ root.cryptoRandomSeed(&seed);
+ } else {
+ fillWithOsEntropy(&seed);
+ }
+
+ wipe_me.gimli = std.crypto.core.Gimli.init(seed);
+
+ // This is at the end so that accidental recursive dependencies result
+ // in stack overflows instead of invalid random data.
+ wipe_me.init_state = .initialized;
+
+ return fillWithCsprng(buffer);
+}
diff --git a/lib/std/crypto/utils.zig b/lib/std/crypto/utils.zig
index 33ad6360f6..08271ac9f4 100644
--- a/lib/std/crypto/utils.zig
+++ b/lib/std/crypto/utils.zig
@@ -51,8 +51,8 @@ pub fn secureZero(comptime T: type, s: []T) void {
test "crypto.utils.timingSafeEql" {
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
- try std.crypto.randomBytes(a[0..]);
- try std.crypto.randomBytes(b[0..]);
+ std.crypto.random.bytes(a[0..]);
+ std.crypto.random.bytes(b[0..]);
testing.expect(!timingSafeEql([100]u8, a, b));
mem.copy(u8, a[0..], b[0..]);
testing.expect(timingSafeEql([100]u8, a, b));
@@ -61,8 +61,8 @@ test "crypto.utils.timingSafeEql" {
test "crypto.utils.timingSafeEql (vectors)" {
var a: [100]u8 = undefined;
var b: [100]u8 = undefined;
- try std.crypto.randomBytes(a[0..]);
- try std.crypto.randomBytes(b[0..]);
+ std.crypto.random.bytes(a[0..]);
+ std.crypto.random.bytes(b[0..]);
const v1: std.meta.Vector(100, u8) = a;
const v2: std.meta.Vector(100, u8) = b;
testing.expect(!timingSafeEql(std.meta.Vector(100, u8), v1, v2));
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index 4f0f44d1b7..7284237cb2 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -1696,6 +1696,7 @@ fn getDebugInfoAllocator() *mem.Allocator {
pub const have_segfault_handling_support = switch (builtin.os.tag) {
.linux, .netbsd => true,
.windows => true,
+ .freebsd, .openbsd => @hasDecl(os, "ucontext_t"),
else => false,
};
pub const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
@@ -1721,7 +1722,7 @@ pub fn attachSegfaultHandler() void {
return;
}
var act = os.Sigaction{
- .sigaction = handleSegfaultLinux,
+ .handler = .{ .sigaction = handleSegfaultLinux },
.mask = os.empty_sigset,
.flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND),
};
@@ -1740,7 +1741,7 @@ fn resetSegfaultHandler() void {
return;
}
var act = os.Sigaction{
- .sigaction = os.SIG_DFL,
+ .handler = .{ .sigaction = os.SIG_DFL },
.mask = os.empty_sigset,
.flags = 0,
};
@@ -1757,7 +1758,9 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
const addr = switch (builtin.os.tag) {
.linux => @ptrToInt(info.fields.sigfault.addr),
+ .freebsd => @ptrToInt(info.addr),
.netbsd => @ptrToInt(info.info.reason.fault.addr),
+ .openbsd => @ptrToInt(info.data.fault.addr),
else => unreachable,
};
@@ -1781,8 +1784,18 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_v
},
.x86_64 => {
const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
- const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]);
- const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]);
+ const ip = switch (builtin.os.tag) {
+ .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]),
+ .freebsd => @intCast(usize, ctx.mcontext.rip),
+ .openbsd => @intCast(usize, ctx.sc_rip),
+ else => unreachable,
+ };
+ const bp = switch (builtin.os.tag) {
+ .linux, .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]),
+ .openbsd => @intCast(usize, ctx.sc_rbp),
+ .freebsd => @intCast(usize, ctx.mcontext.rbp),
+ else => unreachable,
+ };
dumpStackTraceFromBase(bp, ip);
},
.arm => {
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 8b949a57f1..4c346a2c89 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -82,7 +82,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
mem.copy(u8, tmp_path[0..], dirname);
tmp_path[dirname.len] = path.sep;
while (true) {
- try crypto.randomBytes(rand_buf[0..]);
+ crypto.random.bytes(rand_buf[0..]);
base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
if (cwd().symLink(existing_path, tmp_path, .{})) {
@@ -157,7 +157,7 @@ pub const AtomicFile = struct {
tmp_path_buf[base64.Base64Encoder.calcSize(RANDOM_BYTES)] = 0;
while (true) {
- try crypto.randomBytes(rand_buf[0..]);
+ crypto.random.bytes(rand_buf[0..]);
base64_encoder.encode(&tmp_path_buf, &rand_buf);
const file = dir.createFile(
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index e97be007ab..469215e2b3 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -698,7 +698,8 @@ pub const File = struct {
error.FastOpenAlreadyInProgress,
error.MessageTooBig,
error.FileDescriptorNotASocket,
- error.AddressFamilyNotSupported,
+ error.NetworkUnreachable,
+ error.NetworkSubsystemFailed,
=> return self.writeFileAllUnseekable(in_file, args),
else => |e| return e,
diff --git a/lib/std/heap.zig b/lib/std/heap.zig
index a0484d4fdc..fddef10cd3 100644
--- a/lib/std/heap.zig
+++ b/lib/std/heap.zig
@@ -789,7 +789,7 @@ pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) Stack
.fallback_allocator = fallback_allocator,
.fixed_buffer_allocator = undefined,
.allocator = Allocator{
- .allocFn = StackFallbackAllocator(size).realloc,
+ .allocFn = StackFallbackAllocator(size).alloc,
.resizeFn = StackFallbackAllocator(size).resize,
},
};
@@ -815,25 +815,25 @@ pub fn StackFallbackAllocator(comptime size: usize) type {
ptr_align: u29,
len_align: u29,
return_address: usize,
- ) error{OutOfMemory}![*]u8 {
+ ) error{OutOfMemory}![]u8 {
const self = @fieldParentPtr(Self, "allocator", allocator);
- return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, ptr_align) catch
- return fallback_allocator.alloc(len, ptr_align);
+ return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator.allocator, len, ptr_align, len_align, return_address) catch
+ return self.fallback_allocator.allocFn(self.fallback_allocator, len, ptr_align, len_align, return_address);
}
fn resize(
- self: *Allocator,
+ allocator: *Allocator,
buf: []u8,
buf_align: u29,
new_len: usize,
len_align: u29,
return_address: usize,
- ) error{OutOfMemory}!void {
+ ) error{OutOfMemory}!usize {
const self = @fieldParentPtr(Self, "allocator", allocator);
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
- try self.fixed_buffer_allocator.resize(buf, new_len);
+ return FixedBufferAllocator.resize(&self.fixed_buffer_allocator.allocator, buf, buf_align, new_len, len_align, return_address);
} else {
- try self.fallback_allocator.resize(buf, new_len);
+ return self.fallback_allocator.resizeFn(self.fallback_allocator, buf, buf_align, new_len, len_align, return_address);
}
}
};
@@ -970,6 +970,16 @@ test "FixedBufferAllocator.reset" {
testing.expect(y.* == Y);
}
+test "StackFallbackAllocator" {
+ const fallback_allocator = page_allocator;
+ var stack_allocator = stackFallback(4096, fallback_allocator);
+
+ try testAllocator(stack_allocator.get());
+ try testAllocatorAligned(stack_allocator.get());
+ try testAllocatorLargeAlignment(stack_allocator.get());
+ try testAllocatorAlignedShrink(stack_allocator.get());
+}
+
test "FixedBufferAllocator Reuse memory on realloc" {
var small_fixed_buffer: [10]u8 = undefined;
// check if we re-use the memory
diff --git a/lib/std/io.zig b/lib/std/io.zig
index 3f02128a6c..103c443dd6 100644
--- a/lib/std/io.zig
+++ b/lib/std/io.zig
@@ -143,6 +143,8 @@ pub const cOutStream = cWriter;
pub const CountingWriter = @import("io/counting_writer.zig").CountingWriter;
pub const countingWriter = @import("io/counting_writer.zig").countingWriter;
+pub const CountingReader = @import("io/counting_reader.zig").CountingReader;
+pub const countingReader = @import("io/counting_reader.zig").countingReader;
/// Deprecated: use `CountingWriter`
pub const CountingOutStream = CountingWriter;
/// Deprecated: use `countingWriter`
@@ -178,14 +180,6 @@ pub const changeDetectionStream = @import("io/change_detection_stream.zig").chan
pub const FindByteOutStream = @import("io/find_byte_out_stream.zig").FindByteOutStream;
pub const findByteOutStream = @import("io/find_byte_out_stream.zig").findByteOutStream;
-pub const Packing = @import("io/serialization.zig").Packing;
-
-pub const Serializer = @import("io/serialization.zig").Serializer;
-pub const serializer = @import("io/serialization.zig").serializer;
-
-pub const Deserializer = @import("io/serialization.zig").Deserializer;
-pub const deserializer = @import("io/serialization.zig").deserializer;
-
pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAtomicFile;
pub const StreamSource = @import("io/stream_source.zig").StreamSource;
@@ -220,7 +214,6 @@ test "" {
_ = @import("io/writer.zig");
_ = @import("io/peek_stream.zig");
_ = @import("io/seekable_stream.zig");
- _ = @import("io/serialization.zig");
_ = @import("io/stream_source.zig");
_ = @import("io/test.zig");
}
diff --git a/lib/std/io/counting_reader.zig b/lib/std/io/counting_reader.zig
new file mode 100644
index 0000000000..09a624952f
--- /dev/null
+++ b/lib/std/io/counting_reader.zig
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const io = std.io;
+const testing = std.testing;
+
+/// A Reader that counts how many bytes has been read from it.
+pub fn CountingReader(comptime ReaderType: anytype) type {
+ return struct {
+ child_reader: ReaderType,
+ bytes_read: u64 = 0,
+
+ pub const Error = ReaderType.Error;
+ pub const Reader = io.Reader(*@This(), Error, read);
+
+ pub fn read(self: *@This(), buf: []u8) Error!usize {
+ const amt = try self.child_reader.read(buf);
+ self.bytes_read += amt;
+ return amt;
+ }
+
+ pub fn reader(self: *@This()) Reader {
+ return .{ .context = self };
+ }
+ };
+}
+
+pub fn countingReader(reader: anytype) CountingReader(@TypeOf(reader)) {
+ return .{ .child_reader = reader, };
+}
+
+test "io.CountingReader" {
+ const bytes = "yay" ** 100;
+ var fbs = io.fixedBufferStream(bytes);
+
+ var counting_stream = countingReader(fbs.reader());
+ const stream = counting_stream.reader();
+
+ //read and discard all bytes
+ while(stream.readByte()) |_| {} else |err| {
+ testing.expect(err == error.EndOfStream);
+ }
+
+ testing.expect(counting_stream.bytes_read == bytes.len);
+} \ No newline at end of file
diff --git a/lib/std/io/serialization.zig b/lib/std/io/serialization.zig
deleted file mode 100644
index 3fbc203242..0000000000
--- a/lib/std/io/serialization.zig
+++ /dev/null
@@ -1,616 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("../std.zig");
-const builtin = std.builtin;
-const io = std.io;
-const assert = std.debug.assert;
-const math = std.math;
-const meta = std.meta;
-const trait = meta.trait;
-const testing = std.testing;
-
-pub const Packing = enum {
- /// Pack data to byte alignment
- Byte,
-
- /// Pack data to bit alignment
- Bit,
-};
-
-/// Creates a deserializer that deserializes types from any stream.
-/// If `is_packed` is true, the data stream is treated as bit-packed,
-/// otherwise data is expected to be packed to the smallest byte.
-/// Types may implement a custom deserialization routine with a
-/// function named `deserialize` in the form of:
-/// ```
-/// pub fn deserialize(self: *Self, deserializer: anytype) !void
-/// ```
-/// which will be called when the deserializer is used to deserialize
-/// that type. It will pass a pointer to the type instance to deserialize
-/// into and a pointer to the deserializer struct.
-pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime ReaderType: type) type {
- return struct {
- in_stream: if (packing == .Bit) io.BitReader(endian, ReaderType) else ReaderType,
-
- const Self = @This();
-
- pub fn init(in_stream: ReaderType) Self {
- return Self{
- .in_stream = switch (packing) {
- .Bit => io.bitReader(endian, in_stream),
- .Byte => in_stream,
- },
- };
- }
-
- pub fn alignToByte(self: *Self) void {
- if (packing == .Byte) return;
- self.in_stream.alignToByte();
- }
-
- //@BUG: inferred error issue. See: #1386
- fn deserializeInt(self: *Self, comptime T: type) (ReaderType.Error || error{EndOfStream})!T {
- comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
-
- const u8_bit_count = 8;
- const t_bit_count = comptime meta.bitCount(T);
-
- const U = std.meta.Int(.unsigned, t_bit_count);
- const Log2U = math.Log2Int(U);
- const int_size = (t_bit_count + 7) / 8;
-
- if (packing == .Bit) {
- const result = try self.in_stream.readBitsNoEof(U, t_bit_count);
- return @bitCast(T, result);
- }
-
- var buffer: [int_size]u8 = undefined;
- const read_size = try self.in_stream.read(buffer[0..]);
- if (read_size < int_size) return error.EndOfStream;
-
- if (int_size == 1) {
- if (t_bit_count == 8) return @bitCast(T, buffer[0]);
- const PossiblySignedByte = std.meta.Int(@typeInfo(T).Int.signedness, 8);
- return @truncate(T, @bitCast(PossiblySignedByte, buffer[0]));
- }
-
- var result = @as(U, 0);
- for (buffer) |byte, i| {
- switch (endian) {
- .Big => {
- result = (result << u8_bit_count) | byte;
- },
- .Little => {
- result |= @as(U, byte) << @intCast(Log2U, u8_bit_count * i);
- },
- }
- }
-
- return @bitCast(T, result);
- }
-
- /// Deserializes and returns data of the specified type from the stream
- pub fn deserialize(self: *Self, comptime T: type) !T {
- var value: T = undefined;
- try self.deserializeInto(&value);
- return value;
- }
-
- /// Deserializes data into the type pointed to by `ptr`
- pub fn deserializeInto(self: *Self, ptr: anytype) !void {
- const T = @TypeOf(ptr);
- comptime assert(trait.is(.Pointer)(T));
-
- if (comptime trait.isSlice(T) or comptime trait.isPtrTo(.Array)(T)) {
- for (ptr) |*v|
- try self.deserializeInto(v);
- return;
- }
-
- comptime assert(trait.isSingleItemPtr(T));
-
- const C = comptime meta.Child(T);
- const child_type_id = @typeInfo(C);
-
- //custom deserializer: fn(self: *Self, deserializer: anytype) !void
- if (comptime trait.hasFn("deserialize")(C)) return C.deserialize(ptr, self);
-
- if (comptime trait.isPacked(C) and packing != .Bit) {
- var packed_deserializer = deserializer(endian, .Bit, self.in_stream);
- return packed_deserializer.deserializeInto(ptr);
- }
-
- switch (child_type_id) {
- .Void => return,
- .Bool => ptr.* = (try self.deserializeInt(u1)) > 0,
- .Float, .Int => ptr.* = try self.deserializeInt(C),
- .Struct => {
- const info = @typeInfo(C).Struct;
-
- inline for (info.fields) |*field_info| {
- const name = field_info.name;
- const FieldType = field_info.field_type;
-
- if (FieldType == void or FieldType == u0) continue;
-
- //it doesn't make any sense to read pointers
- if (comptime trait.is(.Pointer)(FieldType)) {
- @compileError("Will not " ++ "read field " ++ name ++ " of struct " ++
- @typeName(C) ++ " because it " ++ "is of pointer-type " ++
- @typeName(FieldType) ++ ".");
- }
-
- try self.deserializeInto(&@field(ptr, name));
- }
- },
- .Union => {
- const info = @typeInfo(C).Union;
- if (info.tag_type) |TagType| {
- //we avoid duplicate iteration over the enum tags
- // by getting the int directly and casting it without
- // safety. If it is bad, it will be caught anyway.
- const TagInt = @TagType(TagType);
- const tag = try self.deserializeInt(TagInt);
-
- inline for (info.fields) |field_info| {
- if (@enumToInt(@field(TagType, field_info.name)) == tag) {
- const name = field_info.name;
- const FieldType = field_info.field_type;
- ptr.* = @unionInit(C, name, undefined);
- try self.deserializeInto(&@field(ptr, name));
- return;
- }
- }
- //This is reachable if the enum data is bad
- return error.InvalidEnumTag;
- }
- @compileError("Cannot meaningfully deserialize " ++ @typeName(C) ++
- " because it is an untagged union. Use a custom deserialize().");
- },
- .Optional => {
- const OC = comptime meta.Child(C);
- const exists = (try self.deserializeInt(u1)) > 0;
- if (!exists) {
- ptr.* = null;
- return;
- }
-
- ptr.* = @as(OC, undefined); //make it non-null so the following .? is guaranteed safe
- const val_ptr = &ptr.*.?;
- try self.deserializeInto(val_ptr);
- },
- .Enum => {
- var value = try self.deserializeInt(@TagType(C));
- ptr.* = try meta.intToEnum(C, value);
- },
- else => {
- @compileError("Cannot deserialize " ++ @tagName(child_type_id) ++ " types (unimplemented).");
- },
- }
- }
- };
-}
-
-pub fn deserializer(
- comptime endian: builtin.Endian,
- comptime packing: Packing,
- in_stream: anytype,
-) Deserializer(endian, packing, @TypeOf(in_stream)) {
- return Deserializer(endian, packing, @TypeOf(in_stream)).init(in_stream);
-}
-
-/// Creates a serializer that serializes types to any stream.
-/// If `is_packed` is true, the data will be bit-packed into the stream.
-/// Note that the you must call `serializer.flush()` when you are done
-/// writing bit-packed data in order ensure any unwritten bits are committed.
-/// If `is_packed` is false, data is packed to the smallest byte. In the case
-/// of packed structs, the struct will written bit-packed and with the specified
-/// endianess, after which data will resume being written at the next byte boundary.
-/// Types may implement a custom serialization routine with a
-/// function named `serialize` in the form of:
-/// ```
-/// pub fn serialize(self: Self, serializer: anytype) !void
-/// ```
-/// which will be called when the serializer is used to serialize that type. It will
-/// pass a const pointer to the type instance to be serialized and a pointer
-/// to the serializer struct.
-pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, comptime OutStreamType: type) type {
- return struct {
- out_stream: if (packing == .Bit) io.BitOutStream(endian, OutStreamType) else OutStreamType,
-
- const Self = @This();
- pub const Error = OutStreamType.Error;
-
- pub fn init(out_stream: OutStreamType) Self {
- return Self{
- .out_stream = switch (packing) {
- .Bit => io.bitOutStream(endian, out_stream),
- .Byte => out_stream,
- },
- };
- }
-
- /// Flushes any unwritten bits to the stream
- pub fn flush(self: *Self) Error!void {
- if (packing == .Bit) return self.out_stream.flushBits();
- }
-
- fn serializeInt(self: *Self, value: anytype) Error!void {
- const T = @TypeOf(value);
- comptime assert(trait.is(.Int)(T) or trait.is(.Float)(T));
-
- const t_bit_count = comptime meta.bitCount(T);
- const u8_bit_count = comptime meta.bitCount(u8);
-
- const U = std.meta.Int(.unsigned, t_bit_count);
- const Log2U = math.Log2Int(U);
- const int_size = (t_bit_count + 7) / 8;
-
- const u_value = @bitCast(U, value);
-
- if (packing == .Bit) return self.out_stream.writeBits(u_value, t_bit_count);
-
- var buffer: [int_size]u8 = undefined;
- if (int_size == 1) buffer[0] = u_value;
-
- for (buffer) |*byte, i| {
- const idx = switch (endian) {
- .Big => int_size - i - 1,
- .Little => i,
- };
- const shift = @intCast(Log2U, idx * u8_bit_count);
- const v = u_value >> shift;
- byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
- }
-
- try self.out_stream.writeAll(&buffer);
- }
-
- /// Serializes the passed value into the stream
- pub fn serialize(self: *Self, value: anytype) Error!void {
- const T = comptime @TypeOf(value);
-
- if (comptime trait.isIndexable(T)) {
- for (value) |v|
- try self.serialize(v);
- return;
- }
-
- //custom serializer: fn(self: Self, serializer: anytype) !void
- if (comptime trait.hasFn("serialize")(T)) return T.serialize(value, self);
-
- if (comptime trait.isPacked(T) and packing != .Bit) {
- var packed_serializer = Serializer(endian, .Bit, OutStreamType).init(self.out_stream);
- try packed_serializer.serialize(value);
- try packed_serializer.flush();
- return;
- }
-
- switch (@typeInfo(T)) {
- .Void => return,
- .Bool => try self.serializeInt(@as(u1, @boolToInt(value))),
- .Float, .Int => try self.serializeInt(value),
- .Struct => {
- const info = @typeInfo(T);
-
- inline for (info.Struct.fields) |*field_info| {
- const name = field_info.name;
- const FieldType = field_info.field_type;
-
- if (FieldType == void or FieldType == u0) continue;
-
- //It doesn't make sense to write pointers
- if (comptime trait.is(.Pointer)(FieldType)) {
- @compileError("Will not " ++ "serialize field " ++ name ++
- " of struct " ++ @typeName(T) ++ " because it " ++
- "is of pointer-type " ++ @typeName(FieldType) ++ ".");
- }
- try self.serialize(@field(value, name));
- }
- },
- .Union => {
- const info = @typeInfo(T).Union;
- if (info.tag_type) |TagType| {
- const active_tag = meta.activeTag(value);
- try self.serialize(active_tag);
- //This inline loop is necessary because active_tag is a runtime
- // value, but @field requires a comptime value. Our alternative
- // is to check each field for a match
- inline for (info.fields) |field_info| {
- if (@field(TagType, field_info.name) == active_tag) {
- const name = field_info.name;
- const FieldType = field_info.field_type;
- try self.serialize(@field(value, name));
- return;
- }
- }
- unreachable;
- }
- @compileError("Cannot meaningfully serialize " ++ @typeName(T) ++
- " because it is an untagged union. Use a custom serialize().");
- },
- .Optional => {
- if (value == null) {
- try self.serializeInt(@as(u1, @boolToInt(false)));
- return;
- }
- try self.serializeInt(@as(u1, @boolToInt(true)));
-
- const OC = comptime meta.Child(T);
- const val_ptr = &value.?;
- try self.serialize(val_ptr.*);
- },
- .Enum => {
- try self.serializeInt(@enumToInt(value));
- },
- else => @compileError("Cannot serialize " ++ @tagName(@typeInfo(T)) ++ " types (unimplemented)."),
- }
- }
- };
-}
-
-pub fn serializer(
- comptime endian: builtin.Endian,
- comptime packing: Packing,
- out_stream: anytype,
-) Serializer(endian, packing, @TypeOf(out_stream)) {
- return Serializer(endian, packing, @TypeOf(out_stream)).init(out_stream);
-}
-
-fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
- @setEvalBranchQuota(1500);
- //@NOTE: if this test is taking too long, reduce the maximum tested bitsize
- const max_test_bitsize = 128;
-
- const total_bytes = comptime blk: {
- var bytes = 0;
- comptime var i = 0;
- while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0);
- break :blk bytes * 2;
- };
-
- var data_mem: [total_bytes]u8 = undefined;
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- comptime var i = 0;
- inline while (i <= max_test_bitsize) : (i += 1) {
- const U = std.meta.Int(.unsigned, i);
- const S = std.meta.Int(.signed, i);
- try _serializer.serializeInt(@as(U, i));
- if (i != 0) try _serializer.serializeInt(@as(S, -1)) else try _serializer.serialize(@as(S, 0));
- }
- try _serializer.flush();
-
- i = 0;
- inline while (i <= max_test_bitsize) : (i += 1) {
- const U = std.meta.Int(.unsigned, i);
- const S = std.meta.Int(.signed, i);
- const x = try _deserializer.deserializeInt(U);
- const y = try _deserializer.deserializeInt(S);
- testing.expect(x == @as(U, i));
- if (i != 0) testing.expect(y == @as(S, -1)) else testing.expect(y == 0);
- }
-
- const u8_bit_count = comptime meta.bitCount(u8);
- //0 + 1 + 2 + ... n = (n * (n + 1)) / 2
- //and we have each for unsigned and signed, so * 2
- const total_bits = (max_test_bitsize * (max_test_bitsize + 1));
- const extra_packed_byte = @boolToInt(total_bits % u8_bit_count > 0);
- const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
-
- testing.expect(in.pos == if (packing == .Bit) total_packed_bytes else total_bytes);
-
- //Verify that empty error set works with serializer.
- //deserializer is covered by FixedBufferStream
- var null_serializer = io.serializer(endian, packing, std.io.null_out_stream);
- try null_serializer.serialize(data_mem[0..]);
- try null_serializer.flush();
-}
-
-test "Serializer/Deserializer Int" {
- try testIntSerializerDeserializer(.Big, .Byte);
- try testIntSerializerDeserializer(.Little, .Byte);
- // TODO these tests are disabled due to tripping an LLVM assertion
- // https://github.com/ziglang/zig/issues/2019
- //try testIntSerializerDeserializer(builtin.Endian.Big, true);
- //try testIntSerializerDeserializer(builtin.Endian.Little, true);
-}
-
-fn testIntSerializerDeserializerInfNaN(
- comptime endian: builtin.Endian,
- comptime packing: io.Packing,
-) !void {
- const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8);
- var data_mem: [mem_size]u8 = undefined;
-
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- //@TODO: isInf/isNan not currently implemented for f128.
- try _serializer.serialize(std.math.nan(f16));
- try _serializer.serialize(std.math.inf(f16));
- try _serializer.serialize(std.math.nan(f32));
- try _serializer.serialize(std.math.inf(f32));
- try _serializer.serialize(std.math.nan(f64));
- try _serializer.serialize(std.math.inf(f64));
- //try serializer.serialize(std.math.nan(f128));
- //try serializer.serialize(std.math.inf(f128));
- const nan_check_f16 = try _deserializer.deserialize(f16);
- const inf_check_f16 = try _deserializer.deserialize(f16);
- const nan_check_f32 = try _deserializer.deserialize(f32);
- _deserializer.alignToByte();
- const inf_check_f32 = try _deserializer.deserialize(f32);
- const nan_check_f64 = try _deserializer.deserialize(f64);
- const inf_check_f64 = try _deserializer.deserialize(f64);
- //const nan_check_f128 = try deserializer.deserialize(f128);
- //const inf_check_f128 = try deserializer.deserialize(f128);
- testing.expect(std.math.isNan(nan_check_f16));
- testing.expect(std.math.isInf(inf_check_f16));
- testing.expect(std.math.isNan(nan_check_f32));
- testing.expect(std.math.isInf(inf_check_f32));
- testing.expect(std.math.isNan(nan_check_f64));
- testing.expect(std.math.isInf(inf_check_f64));
- //expect(std.math.isNan(nan_check_f128));
- //expect(std.math.isInf(inf_check_f128));
-}
-
-test "Serializer/Deserializer Int: Inf/NaN" {
- try testIntSerializerDeserializerInfNaN(.Big, .Byte);
- try testIntSerializerDeserializerInfNaN(.Little, .Byte);
- try testIntSerializerDeserializerInfNaN(.Big, .Bit);
- try testIntSerializerDeserializerInfNaN(.Little, .Bit);
-}
-
-fn testAlternateSerializer(self: anytype, _serializer: anytype) !void {
- try _serializer.serialize(self.f_f16);
-}
-
-fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
- const ColorType = enum(u4) {
- RGB8 = 1,
- RA16 = 2,
- R32 = 3,
- };
-
- const TagAlign = union(enum(u32)) {
- A: u8,
- B: u8,
- C: u8,
- };
-
- const Color = union(ColorType) {
- RGB8: struct {
- r: u8,
- g: u8,
- b: u8,
- a: u8,
- },
- RA16: struct {
- r: u16,
- a: u16,
- },
- R32: u32,
- };
-
- const PackedStruct = packed struct {
- f_i3: i3,
- f_u2: u2,
- };
-
- //to test custom serialization
- const Custom = struct {
- f_f16: f16,
- f_unused_u32: u32,
-
- pub fn deserialize(self: *@This(), _deserializer: anytype) !void {
- try _deserializer.deserializeInto(&self.f_f16);
- self.f_unused_u32 = 47;
- }
-
- pub const serialize = testAlternateSerializer;
- };
-
- const MyStruct = struct {
- f_i3: i3,
- f_u8: u8,
- f_tag_align: TagAlign,
- f_u24: u24,
- f_i19: i19,
- f_void: void,
- f_f32: f32,
- f_f128: f128,
- f_packed_0: PackedStruct,
- f_i7arr: [10]i7,
- f_of64n: ?f64,
- f_of64v: ?f64,
- f_color_type: ColorType,
- f_packed_1: PackedStruct,
- f_custom: Custom,
- f_color: Color,
- };
-
- const my_inst = MyStruct{
- .f_i3 = -1,
- .f_u8 = 8,
- .f_tag_align = TagAlign{ .B = 148 },
- .f_u24 = 24,
- .f_i19 = 19,
- .f_void = {},
- .f_f32 = 32.32,
- .f_f128 = 128.128,
- .f_packed_0 = PackedStruct{ .f_i3 = -1, .f_u2 = 2 },
- .f_i7arr = [10]i7{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
- .f_of64n = null,
- .f_of64v = 64.64,
- .f_color_type = ColorType.R32,
- .f_packed_1 = PackedStruct{ .f_i3 = 1, .f_u2 = 1 },
- .f_custom = Custom{ .f_f16 = 38.63, .f_unused_u32 = 47 },
- .f_color = Color{ .R32 = 123822 },
- };
-
- var data_mem: [@sizeOf(MyStruct)]u8 = undefined;
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- try _serializer.serialize(my_inst);
-
- const my_copy = try _deserializer.deserialize(MyStruct);
- testing.expect(meta.eql(my_copy, my_inst));
-}
-
-test "Serializer/Deserializer generic" {
- try testSerializerDeserializer(builtin.Endian.Big, .Byte);
- try testSerializerDeserializer(builtin.Endian.Little, .Byte);
- try testSerializerDeserializer(builtin.Endian.Big, .Bit);
- try testSerializerDeserializer(builtin.Endian.Little, .Bit);
-}
-
-fn testBadData(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
- const E = enum(u14) {
- One = 1,
- Two = 2,
- };
-
- const A = struct {
- e: E,
- };
-
- const C = union(E) {
- One: u14,
- Two: f16,
- };
-
- var data_mem: [4]u8 = undefined;
- var out = io.fixedBufferStream(&data_mem);
- var _serializer = serializer(endian, packing, out.outStream());
-
- var in = io.fixedBufferStream(&data_mem);
- var _deserializer = deserializer(endian, packing, in.reader());
-
- try _serializer.serialize(@as(u14, 3));
- testing.expectError(error.InvalidEnumTag, _deserializer.deserialize(A));
- out.pos = 0;
- try _serializer.serialize(@as(u14, 3));
- try _serializer.serialize(@as(u14, 88));
- testing.expectError(error.InvalidEnumTag, _deserializer.deserialize(C));
-}
-
-test "Deserializer bad data" {
- try testBadData(.Big, .Byte);
- try testBadData(.Little, .Byte);
- try testBadData(.Big, .Bit);
- try testBadData(.Little, .Bit);
-}
diff --git a/lib/std/macho.zig b/lib/std/macho.zig
index ec0d23cd92..016590e36b 100644
--- a/lib/std/macho.zig
+++ b/lib/std/macho.zig
@@ -1257,6 +1257,33 @@ pub const VM_PROT_WRITE: vm_prot_t = 0x2;
/// VM execute permission
pub const VM_PROT_EXECUTE: vm_prot_t = 0x4;
+pub const BIND_TYPE_POINTER: u8 = 1;
+pub const BIND_TYPE_TEXT_ABSOLUTE32: u8 = 2;
+pub const BIND_TYPE_TEXT_PCREL32: u8 = 3;
+
+pub const BIND_SPECIAL_DYLIB_SELF: i8 = 0;
+pub const BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE: i8 = -1;
+pub const BIND_SPECIAL_DYLIB_FLAT_LOOKUP: i8 = -2;
+
+pub const BIND_SYMBOL_FLAGS_WEAK_IMPORT: u8 = 0x1;
+pub const BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION: u8 = 0x8;
+
+pub const BIND_OPCODE_MASK: u8 = 0xf0;
+pub const BIND_IMMEDIATE_MASK: u8 = 0x0f;
+pub const BIND_OPCODE_DONE: u8 = 0x00;
+pub const BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: u8 = 0x10;
+pub const BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: u8 = 0x20;
+pub const BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: u8 = 0x30;
+pub const BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: u8 = 0x40;
+pub const BIND_OPCODE_SET_TYPE_IMM: u8 = 0x50;
+pub const BIND_OPCODE_SET_ADDEND_SLEB: u8 = 0x60;
+pub const BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: u8 = 0x70;
+pub const BIND_OPCODE_ADD_ADDR_ULEB: 0x80;
+pub const BIND_OPCODE_DO_BIND: u8 = 0x90;
+pub const BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: u8 = 0xa0;
+pub const BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: u8 = 0xb0;
+pub const BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: u8 = xc0;
+
pub const reloc_type_x86_64 = packed enum(u4) {
/// for absolute addresses
X86_64_RELOC_UNSIGNED = 0,
diff --git a/lib/std/math.zig b/lib/std/math.zig
index 4fc9eb40e6..a51cac6e7d 100644
--- a/lib/std/math.zig
+++ b/lib/std/math.zig
@@ -1169,7 +1169,7 @@ pub const Order = enum {
return switch (self) {
.lt => .gt,
.eq => .eq,
- .gt => .gt,
+ .gt => .lt,
};
}
@@ -1266,6 +1266,29 @@ test "compare between signed and unsigned" {
testing.expect(compare(@as(u8, 1), .eq, @as(u8, 1)));
}
+test "order" {
+ testing.expect(order(0, 0) == .eq);
+ testing.expect(order(1, 0) == .gt);
+ testing.expect(order(-1, 0) == .lt);
+}
+
+test "order.invert" {
+ testing.expect(Order.invert(order(0, 0)) == .eq);
+ testing.expect(Order.invert(order(1, 0)) == .lt);
+ testing.expect(Order.invert(order(-1, 0)) == .gt);
+}
+
+test "order.compare" {
+ testing.expect(order(-1, 0).compare(.lt));
+ testing.expect(order(-1, 0).compare(.lte));
+ testing.expect(order(0, 0).compare(.lte));
+ testing.expect(order(0, 0).compare(.eq));
+ testing.expect(order(0, 0).compare(.gte));
+ testing.expect(order(1, 0).compare(.gte));
+ testing.expect(order(1, 0).compare(.gt));
+ testing.expect(order(1, 0).compare(.neq));
+}
+
test "math.comptime" {
comptime const v = sin(@as(f32, 1)) + ln(@as(f32, 5));
testing.expect(v == sin(@as(f32, 1)) + ln(@as(f32, 5)));
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
index 2cf7f6de81..d51a2744b3 100644
--- a/lib/std/meta.zig
+++ b/lib/std/meta.zig
@@ -9,6 +9,7 @@ const debug = std.debug;
const mem = std.mem;
const math = std.math;
const testing = std.testing;
+const root = @import("root");
pub const trait = @import("meta/trait.zig");
pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags;
@@ -1085,3 +1086,10 @@ test "Tuple" {
TupleTester.assertTuple(.{ u32, f16 }, Tuple(&[_]type{ u32, f16 }));
TupleTester.assertTuple(.{ u32, f16, []const u8, void }, Tuple(&[_]type{ u32, f16, []const u8, void }));
}
+
+/// TODO: https://github.com/ziglang/zig/issues/425
+pub fn globalOption(comptime name: []const u8, comptime T: type) ?T {
+ if (!@hasDecl(root, name))
+ return null;
+ return @as(T, @field(root, name));
+}
diff --git a/lib/std/os.zig b/lib/std/os.zig
index e3afe90e5d..5a57fed5cf 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -4592,7 +4592,7 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
}
/// Examine and change a signal action.
-pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void {
+pub fn sigaction(sig: u6, act: ?*const Sigaction, oact: ?*Sigaction) void {
switch (errno(system.sigaction(sig, act, oact))) {
0 => return,
EFAULT => unreachable,
@@ -4774,9 +4774,33 @@ pub const SendError = error{
BrokenPipe,
FileDescriptorNotASocket,
- AddressFamilyNotSupported,
+
+ /// Network is unreachable.
+ NetworkUnreachable,
+
+ /// The local network interface used to reach the destination is down.
+ NetworkSubsystemFailed,
} || UnexpectedError;
+pub const SendToError = SendError || error{
+ /// The passed address didn't have the correct address family in its sa_family field.
+ AddressFamilyNotSupported,
+
+ /// Returned when socket is AF_UNIX and the given path has a symlink loop.
+ SymLinkLoop,
+
+ /// Returned when socket is AF_UNIX and the given path length exceeds `MAX_PATH_BYTES` bytes.
+ NameTooLong,
+
+ /// Returned when socket is AF_UNIX and the given path does not point to an existing file.
+ FileNotFound,
+ NotDir,
+
+ /// The socket is not connected (connection-oriented sockets only).
+ SocketNotConnected,
+ AddressNotAvailable,
+};
+
/// Transmit a message to another socket.
///
/// The `sendto` call may be used only when the socket is in a connected state (so that the intended
@@ -4810,19 +4834,31 @@ pub fn sendto(
flags: u32,
dest_addr: ?*const sockaddr,
addrlen: socklen_t,
-) SendError!usize {
+) SendToError!usize {
while (true) {
const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
if (builtin.os.tag == .windows) {
if (rc == windows.ws2_32.SOCKET_ERROR) {
switch (windows.ws2_32.WSAGetLastError()) {
.WSAEACCES => return error.AccessDenied,
+ .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
.WSAECONNRESET => return error.ConnectionResetByPeer,
.WSAEMSGSIZE => return error.MessageTooBig,
.WSAENOBUFS => return error.SystemResources,
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
- // TODO: handle more errors
+ .WSAEDESTADDRREQ => unreachable, // A destination address is required.
+ .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
+ .WSAEHOSTUNREACH => return error.NetworkUnreachable,
+ // TODO: WSAEINPROGRESS, WSAEINTR
+ .WSAEINVAL => unreachable,
+ .WSAENETDOWN => return error.NetworkSubsystemFailed,
+ .WSAENETRESET => return error.ConnectionResetByPeer,
+ .WSAENETUNREACH => return error.NetworkUnreachable,
+ .WSAENOTCONN => return error.SocketNotConnected,
+ .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
+ .WSAEWOULDBLOCK => return error.WouldBlock,
+ .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
else => |err| return windows.unexpectedWSAError(err),
}
} else {
@@ -4845,11 +4881,18 @@ pub fn sendto(
EMSGSIZE => return error.MessageTooBig,
ENOBUFS => return error.SystemResources,
ENOMEM => return error.SystemResources,
- ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
EPIPE => return error.BrokenPipe,
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
+ ELOOP => return error.SymLinkLoop,
+ ENAMETOOLONG => return error.NameTooLong,
+ ENOENT => return error.FileNotFound,
+ ENOTDIR => return error.NotDir,
+ EHOSTUNREACH => return error.NetworkUnreachable,
+ ENETUNREACH => return error.NetworkUnreachable,
+ ENOTCONN => return error.SocketNotConnected,
+ ENETDOWN => return error.NetworkSubsystemFailed,
else => |err| return unexpectedErrno(err),
}
}
@@ -4881,7 +4924,16 @@ pub fn send(
buf: []const u8,
flags: u32,
) SendError!usize {
- return sendto(sockfd, buf, flags, null, 0);
+ return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
+ error.AddressFamilyNotSupported => unreachable,
+ error.SymLinkLoop => unreachable,
+ error.NameTooLong => unreachable,
+ error.FileNotFound => unreachable,
+ error.NotDir => unreachable,
+ error.NetworkUnreachable => unreachable,
+ error.AddressNotAvailable => unreachable,
+ else => |e| return e,
+ };
}
pub const SendFileError = PReadError || WriteError || SendError;
@@ -5803,3 +5855,51 @@ pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void
else => |err| return unexpectedErrno(err),
}
}
+
+pub const MadviseError = error{
+ /// advice is MADV_REMOVE, but the specified address range is not a shared writable mapping.
+ AccessDenied,
+ /// advice is MADV_HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
+ PermissionDenied,
+ /// A kernel resource was temporarily unavailable.
+ SystemResources,
+ /// One of the following:
+ /// * addr is not page-aligned or length is negative
+ /// * advice is not valid
+ /// * advice is MADV_DONTNEED or MADV_REMOVE and the specified address range
+ /// includes locked, Huge TLB pages, or VM_PFNMAP pages.
+ /// * advice is MADV_MERGEABLE or MADV_UNMERGEABLE, but the kernel was not
+ /// configured with CONFIG_KSM.
+ /// * advice is MADV_FREE or MADV_WIPEONFORK but the specified address range
+ /// includes file, Huge TLB, MAP_SHARED, or VM_PFNMAP ranges.
+ InvalidSyscall,
+ /// (for MADV_WILLNEED) Paging in this area would exceed the process's
+ /// maximum resident set size.
+ WouldExceedMaximumResidentSetSize,
+ /// One of the following:
+ /// * (for MADV_WILLNEED) Not enough memory: paging in failed.
+ /// * Addresses in the specified range are not currently mapped, or
+ /// are outside the address space of the process.
+ OutOfMemory,
+ /// The madvise syscall is not available on this version and configuration
+ /// of the Linux kernel.
+ MadviseUnavailable,
+ /// The operating system returned an undocumented error code.
+ Unexpected,
+};
+
+/// Give advice about use of memory.
+/// This syscall is optional and is sometimes configured to be disabled.
+pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
+ switch (errno(system.madvise(ptr, length, advice))) {
+ 0 => return,
+ EACCES => return error.AccessDenied,
+ EAGAIN => return error.SystemResources,
+ EBADF => unreachable, // The map exists, but the area maps something that isn't a file.
+ EINVAL => return error.InvalidSyscall,
+ EIO => return error.WouldExceedMaximumResidentSetSize,
+ ENOMEM => return error.OutOfMemory,
+ ENOSYS => return error.MadviseUnavailable,
+ else => |err| return unexpectedErrno(err),
+ }
+}
diff --git a/lib/std/os/bits/darwin.zig b/lib/std/os/bits/darwin.zig
index 83ca09a9e1..8bd40ed9a3 100644
--- a/lib/std/os/bits/darwin.zig
+++ b/lib/std/os/bits/darwin.zig
@@ -124,13 +124,40 @@ pub const timespec = extern struct {
};
pub const sigset_t = u32;
-pub const empty_sigset = sigset_t(0);
+pub const empty_sigset: sigset_t = 0;
+
+pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize));
+pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0);
+pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1);
+pub const SIG_HOLD = @intToPtr(?Sigaction.sigaction_fn, 5);
+
+pub const siginfo_t = extern struct {
+ signo: c_int,
+ errno: c_int,
+ code: c_int,
+ pid: pid_t,
+ uid: uid_t,
+ status: c_int,
+ addr: *c_void,
+ value: extern union {
+ int: c_int,
+ ptr: *c_void,
+ },
+ si_band: c_long,
+ _pad: [7]c_ulong,
+};
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with function name.
pub const Sigaction = extern struct {
- handler: fn (c_int) callconv(.C) void,
- sa_mask: sigset_t,
- sa_flags: c_int,
+ pub const handler_fn = fn (c_int) callconv(.C) void;
+ pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void;
+
+ handler: extern union {
+ handler: ?handler_fn,
+ sigaction: ?sigaction_fn,
+ },
+ mask: sigset_t,
+ flags: c_uint,
};
pub const dirent = extern struct {
diff --git a/lib/std/os/bits/dragonfly.zig b/lib/std/os/bits/dragonfly.zig
index 2fd9e39c7b..61b6b9f363 100644
--- a/lib/std/os/bits/dragonfly.zig
+++ b/lib/std/os/bits/dragonfly.zig
@@ -530,12 +530,16 @@ pub const sigset_t = extern struct {
};
pub const sig_atomic_t = c_int;
pub const Sigaction = extern struct {
- __sigaction_u: extern union {
- __sa_handler: ?fn (c_int) callconv(.C) void,
- __sa_sigaction: ?fn (c_int, [*c]siginfo_t, ?*c_void) callconv(.C) void,
+ pub const handler_fn = fn (c_int) callconv(.C) void;
+ pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void;
+
+ /// signal handler
+ handler: extern union {
+ handler: ?handler_fn,
+ sigaction: ?sigaction_fn,
},
- sa_flags: c_int,
- sa_mask: sigset_t,
+ flags: c_uint,
+ mask: sigset_t,
};
pub const sig_t = [*c]fn (c_int) callconv(.C) void;
diff --git a/lib/std/os/bits/freebsd.zig b/lib/std/os/bits/freebsd.zig
index 5dce94bd90..08713bb2e4 100644
--- a/lib/std/os/bits/freebsd.zig
+++ b/lib/std/os/bits/freebsd.zig
@@ -4,6 +4,7 @@
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
const std = @import("../../std.zig");
+const builtin = std.builtin;
const maxInt = std.math.maxInt;
// See https://svnweb.freebsd.org/base/head/sys/sys/_types.h?view=co
@@ -736,23 +737,61 @@ pub const winsize = extern struct {
const NSIG = 32;
-pub const SIG_ERR = @intToPtr(fn (i32) callconv(.C) void, maxInt(usize));
-pub const SIG_DFL = @intToPtr(fn (i32) callconv(.C) void, 0);
-pub const SIG_IGN = @intToPtr(fn (i32) callconv(.C) void, 1);
+pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize));
+pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0);
+pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1);
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
pub const Sigaction = extern struct {
+ pub const handler_fn = fn (c_int) callconv(.C) void;
+ pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void;
+
/// signal handler
- __sigaction_u: extern union {
- __sa_handler: fn (i32) callconv(.C) void,
- __sa_sigaction: fn (i32, *__siginfo, usize) callconv(.C) void,
+ handler: extern union {
+ handler: ?handler_fn,
+ sigaction: ?sigaction_fn,
},
/// see signal options
- sa_flags: u32,
+ flags: c_uint,
/// signal mask to apply
- sa_mask: sigset_t,
+ mask: sigset_t,
+};
+
+pub const siginfo_t = extern struct {
+ signo: c_int,
+ errno: c_int,
+ code: c_int,
+ pid: pid_t,
+ uid: uid_t,
+ status: c_int,
+ addr: ?*c_void,
+ value: sigval,
+ reason: extern union {
+ fault: extern struct {
+ trapno: c_int,
+ },
+ timer: extern struct {
+ timerid: c_int,
+ overrun: c_int,
+ },
+ mesgq: extern struct {
+ mqd: c_int,
+ },
+ poll: extern struct {
+ band: c_long,
+ },
+ spare: extern struct {
+ spare1: c_long,
+ spare2: [7]c_int,
+ },
+ },
+};
+
+pub const sigval = extern union {
+ int: c_int,
+ ptr: ?*c_void,
};
pub const _SIG_WORDS = 4;
@@ -775,6 +814,55 @@ pub const sigset_t = extern struct {
__bits: [_SIG_WORDS]u32,
};
+pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** _SIG_WORDS };
+
+pub usingnamespace switch (builtin.arch) {
+ .x86_64 => struct {
+ pub const ucontext_t = extern struct {
+ sigmask: sigset_t,
+ mcontext: mcontext_t,
+ link: ?*ucontext_t,
+ stack: stack_t,
+ flags: c_int,
+ __spare__: [4]c_int,
+ };
+
+ /// XXX x86_64 specific
+ pub const mcontext_t = extern struct {
+ onstack: u64,
+ rdi: u64,
+ rsi: u64,
+ rdx: u64,
+ rcx: u64,
+ r8: u64,
+ r9: u64,
+ rax: u64,
+ rbx: u64,
+ rbp: u64,
+ r10: u64,
+ r11: u64,
+ r12: u64,
+ r13: u64,
+ r14: u64,
+ r15: u64,
+ trapno: u32,
+ fs: u16,
+ gs: u16,
+ addr: u64,
+ flags: u32,
+ es: u16,
+ ds: u16,
+ err: u64,
+ rip: u64,
+ cs: u64,
+ rflags: u64,
+ rsp: u64,
+ ss: u64,
+ };
+ },
+ else => struct {},
+};
+
pub const EPERM = 1; // Operation not permitted
pub const ENOENT = 2; // No such file or directory
pub const ESRCH = 3; // No such process
diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig
index 6952ab7e0e..2bcfc89ecf 100644
--- a/lib/std/os/bits/linux.zig
+++ b/lib/std/os/bits/linux.zig
@@ -864,27 +864,38 @@ pub const sigset_t = [1024 / 32]u32;
pub const all_mask: sigset_t = [_]u32{0xffffffff} ** sigset_t.len;
pub const app_mask: sigset_t = [2]u32{ 0xfffffffc, 0x7fffffff } ++ [_]u32{0xffffffff} ** 30;
-pub const k_sigaction = if (is_mips)
- extern struct {
- flags: usize,
- sigaction: ?fn (i32, *siginfo_t, ?*c_void) callconv(.C) void,
- mask: [4]u32,
+pub const k_sigaction = switch (builtin.arch) {
+ .mips, .mipsel => extern struct {
+ flags: c_uint,
+ handler: ?fn (c_int) callconv(.C) void,
+ mask: [4]c_ulong,
restorer: fn () callconv(.C) void,
- }
-else
- extern struct {
- sigaction: ?fn (i32, *siginfo_t, ?*c_void) callconv(.C) void,
- flags: usize,
+ },
+ .mips64, .mips64el => extern struct {
+ flags: c_uint,
+ handler: ?fn (c_int) callconv(.C) void,
+ mask: [2]c_ulong,
restorer: fn () callconv(.C) void,
- mask: [2]u32,
- };
+ },
+ else => extern struct {
+ handler: ?fn (c_int) callconv(.C) void,
+ flags: c_ulong,
+ restorer: fn () callconv(.C) void,
+ mask: [2]c_uint,
+ },
+};
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
pub const Sigaction = extern struct {
- pub const sigaction_fn = fn (i32, *siginfo_t, ?*c_void) callconv(.C) void;
- sigaction: ?sigaction_fn,
+ pub const handler_fn = fn (c_int) callconv(.C) void;
+ pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void;
+
+ handler: extern union {
+ handler: ?handler_fn,
+ sigaction: ?sigaction_fn,
+ },
mask: sigset_t,
- flags: u32,
+ flags: c_uint,
restorer: ?fn () callconv(.C) void = null,
};
@@ -1826,6 +1837,39 @@ pub const tcflag_t = u32;
pub const NCCS = 32;
+pub const B0 = 0o0000000;
+pub const B50 = 0o0000001;
+pub const B75 = 0o0000002;
+pub const B110 = 0o0000003;
+pub const B134 = 0o0000004;
+pub const B150 = 0o0000005;
+pub const B200 = 0o0000006;
+pub const B300 = 0o0000007;
+pub const B600 = 0o0000010;
+pub const B1200 = 0o0000011;
+pub const B1800 = 0o0000012;
+pub const B2400 = 0o0000013;
+pub const B4800 = 0o0000014;
+pub const B9600 = 0o0000015;
+pub const B19200 = 0o0000016;
+pub const B38400 = 0o0000017;
+pub const BOTHER = 0o0010000;
+pub const B57600 = 0o0010001;
+pub const B115200 = 0o0010002;
+pub const B230400 = 0o0010003;
+pub const B460800 = 0o0010004;
+pub const B500000 = 0o0010005;
+pub const B576000 = 0o0010006;
+pub const B921600 = 0o0010007;
+pub const B1000000 = 0o0010010;
+pub const B1152000 = 0o0010011;
+pub const B1500000 = 0o0010012;
+pub const B2000000 = 0o0010013;
+pub const B2500000 = 0o0010014;
+pub const B3000000 = 0o0010015;
+pub const B3500000 = 0o0010016;
+pub const B4000000 = 0o0010017;
+
pub const IGNBRK = 1;
pub const BRKINT = 2;
pub const IGNPAR = 4;
@@ -2001,3 +2045,25 @@ pub const rlimit = extern struct {
/// Hard limit
max: rlim_t,
};
+
+pub const MADV_NORMAL = 0;
+pub const MADV_RANDOM = 1;
+pub const MADV_SEQUENTIAL = 2;
+pub const MADV_WILLNEED = 3;
+pub const MADV_DONTNEED = 4;
+pub const MADV_FREE = 8;
+pub const MADV_REMOVE = 9;
+pub const MADV_DONTFORK = 10;
+pub const MADV_DOFORK = 11;
+pub const MADV_MERGEABLE = 12;
+pub const MADV_UNMERGEABLE = 13;
+pub const MADV_HUGEPAGE = 14;
+pub const MADV_NOHUGEPAGE = 15;
+pub const MADV_DONTDUMP = 16;
+pub const MADV_DODUMP = 17;
+pub const MADV_WIPEONFORK = 18;
+pub const MADV_KEEPONFORK = 19;
+pub const MADV_COLD = 20;
+pub const MADV_PAGEOUT = 21;
+pub const MADV_HWPOISON = 100;
+pub const MADV_SOFT_OFFLINE = 101;
diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig
index 5539199c73..d961e7ff4d 100644
--- a/lib/std/os/bits/linux/arm-eabi.zig
+++ b/lib/std/os/bits/linux/arm-eabi.zig
@@ -407,8 +407,11 @@ pub const SYS = extern enum(usize) {
fspick = 433,
pidfd_open = 434,
clone3 = 435,
+ close_range = 436,
openat2 = 437,
pidfd_getfd = 438,
+ faccessat2 = 439,
+ process_madvise = 440,
breakpoint = 0x0f0001,
cacheflush = 0x0f0002,
diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig
index 2f677ca537..71a98e49b7 100644
--- a/lib/std/os/bits/linux/arm64.zig
+++ b/lib/std/os/bits/linux/arm64.zig
@@ -308,8 +308,11 @@ pub const SYS = extern enum(usize) {
fspick = 433,
pidfd_open = 434,
clone3 = 435,
+ close_range = 436,
openat2 = 437,
pidfd_getfd = 438,
+ faccessat2 = 439,
+ process_madvise = 440,
_,
};
diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig
index b8c7221e9e..5e78e5c357 100644
--- a/lib/std/os/bits/linux/i386.zig
+++ b/lib/std/os/bits/linux/i386.zig
@@ -441,8 +441,13 @@ pub const SYS = extern enum(usize) {
fsconfig = 431,
fsmount = 432,
fspick = 433,
+ pidfd_open = 434,
+ clone3 = 435,
+ close_range = 436,
openat2 = 437,
pidfd_getfd = 438,
+ faccessat2 = 439,
+ process_madvise = 440,
_,
};
diff --git a/lib/std/os/bits/linux/powerpc64.zig b/lib/std/os/bits/linux/powerpc64.zig
index 89619ebe16..98fd77997c 100644
--- a/lib/std/os/bits/linux/powerpc64.zig
+++ b/lib/std/os/bits/linux/powerpc64.zig
@@ -404,8 +404,11 @@ pub const SYS = extern enum(usize) {
fspick = 433,
pidfd_open = 434,
clone3 = 435,
+ close_range = 436,
openat2 = 437,
pidfd_getfd = 438,
+ faccessat2 = 439,
+ process_madvise = 440,
_,
};
diff --git a/lib/std/os/bits/linux/riscv64.zig b/lib/std/os/bits/linux/riscv64.zig
index bd4b45f95f..a597f4ff0b 100644
--- a/lib/std/os/bits/linux/riscv64.zig
+++ b/lib/std/os/bits/linux/riscv64.zig
@@ -305,8 +305,11 @@ pub const SYS = extern enum(usize) {
fspick = 433,
pidfd_open = 434,
clone3 = 435,
+ close_range = 436,
openat2 = 437,
pidfd_getfd = 438,
+ faccessat2 = 439,
+ process_madvise = 440,
_,
};
diff --git a/lib/std/os/bits/linux/sparc64.zig b/lib/std/os/bits/linux/sparc64.zig
index 1ce17b3a01..e2e34c39e4 100644
--- a/lib/std/os/bits/linux/sparc64.zig
+++ b/lib/std/os/bits/linux/sparc64.zig
@@ -381,8 +381,12 @@ pub const SYS = extern enum(usize) {
fsmount = 432,
fspick = 433,
pidfd_open = 434,
+ clone3 = 435,
+ close_range = 436,
openat2 = 437,
pidfd_getfd = 438,
+ faccessat2 = 439,
+ process_madvise = 440,
_,
};
diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig
index 19e52536e8..d02f557690 100644
--- a/lib/std/os/bits/linux/x86_64.zig
+++ b/lib/std/os/bits/linux/x86_64.zig
@@ -370,8 +370,11 @@ pub const SYS = extern enum(usize) {
fspick = 433,
pidfd_open = 434,
clone3 = 435,
+ close_range = 436,
openat2 = 437,
pidfd_getfd = 438,
+ faccessat2 = 439,
+ process_madvise = 440,
_,
};
diff --git a/lib/std/os/bits/netbsd.zig b/lib/std/os/bits/netbsd.zig
index 0f4b6b60fa..be25284b73 100644
--- a/lib/std/os/bits/netbsd.zig
+++ b/lib/std/os/bits/netbsd.zig
@@ -716,13 +716,18 @@ pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1);
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
pub const Sigaction = extern struct {
- pub const sigaction_fn = fn (i32, *siginfo_t, ?*c_void) callconv(.C) void;
+ pub const handler_fn = fn (c_int) callconv(.C) void;
+ pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void;
+
/// signal handler
- sigaction: ?sigaction_fn,
+ handler: extern union {
+ handler: ?handler_fn,
+ sigaction: ?sigaction_fn,
+ },
/// signal mask to apply
mask: sigset_t,
/// signal options
- flags: u32,
+ flags: c_uint,
};
pub const sigval_t = extern union {
@@ -800,6 +805,10 @@ pub const sigset_t = extern struct {
__bits: [_SIG_WORDS]u32,
};
+pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize));
+pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0);
+pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1);
+
pub const empty_sigset = sigset_t{ .__bits = [_]u32{0} ** _SIG_WORDS };
// XXX x86_64 specific
diff --git a/lib/std/os/bits/openbsd.zig b/lib/std/os/bits/openbsd.zig
index c85a476e02..c84a6de01a 100644
--- a/lib/std/os/bits/openbsd.zig
+++ b/lib/std/os/bits/openbsd.zig
@@ -61,18 +61,18 @@ pub const Flock = extern struct {
l_start: off_t,
l_len: off_t,
l_pid: pid_t,
- l_type: i16,
- l_whence: i16,
+ l_type: c_short,
+ l_whence: c_short,
};
pub const addrinfo = extern struct {
- flags: i32,
- family: i32,
- socktype: i32,
- protocol: i32,
+ flags: c_int,
+ family: c_int,
+ socktype: c_int,
+ protocol: c_int,
addrlen: socklen_t,
- canonname: ?[*:0]u8,
addr: ?*sockaddr,
+ canonname: ?[*:0]u8,
next: ?*addrinfo,
};
@@ -135,7 +135,7 @@ pub const msghdr = extern struct {
msg_iov: [*]iovec,
/// # elements in msg_iov
- msg_iovlen: i32,
+ msg_iovlen: c_uint,
/// ancillary data
msg_control: ?*c_void,
@@ -144,7 +144,7 @@ pub const msghdr = extern struct {
msg_controllen: socklen_t,
/// flags on received message
- msg_flags: i32,
+ msg_flags: c_int,
};
pub const msghdr_const = extern struct {
@@ -158,7 +158,7 @@ pub const msghdr_const = extern struct {
msg_iov: [*]iovec_const,
/// # elements in msg_iov
- msg_iovlen: i32,
+ msg_iovlen: c_uint,
/// ancillary data
msg_control: ?*c_void,
@@ -167,7 +167,7 @@ pub const msghdr_const = extern struct {
msg_controllen: socklen_t,
/// flags on received message
- msg_flags: i32,
+ msg_flags: c_int,
};
pub const libc_stat = extern struct {
@@ -739,10 +739,10 @@ pub fn WIFSIGNALED(s: u32) bool {
}
pub const winsize = extern struct {
- ws_row: u16,
- ws_col: u16,
- ws_xpixel: u16,
- ws_ypixel: u16,
+ ws_row: c_ushort,
+ ws_col: c_ushort,
+ ws_xpixel: c_ushort,
+ ws_ypixel: c_ushort,
};
const NSIG = 33;
@@ -750,16 +750,23 @@ const NSIG = 33;
pub const SIG_ERR = @intToPtr(?Sigaction.sigaction_fn, maxInt(usize));
pub const SIG_DFL = @intToPtr(?Sigaction.sigaction_fn, 0);
pub const SIG_IGN = @intToPtr(?Sigaction.sigaction_fn, 1);
+pub const SIG_CATCH = @intToPtr(?Sigaction.sigaction_fn, 2);
+pub const SIG_HOLD = @intToPtr(?Sigaction.sigaction_fn, 3);
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
pub const Sigaction = extern struct {
- pub const sigaction_fn = fn (c_int, *siginfo_t, ?*c_void) callconv(.C) void;
+ pub const handler_fn = fn (c_int) callconv(.C) void;
+ pub const sigaction_fn = fn (c_int, *const siginfo_t, ?*const c_void) callconv(.C) void;
+
/// signal handler
- sigaction: ?sigaction_fn,
+ handler: extern union {
+ handler: ?handler_fn,
+ sigaction: ?sigaction_fn,
+ },
/// signal mask to apply
mask: sigset_t,
/// signal options
- flags: c_int,
+ flags: c_uint,
};
pub const sigval = extern union {
@@ -767,33 +774,97 @@ pub const sigval = extern union {
ptr: ?*c_void,
};
-pub const siginfo_t = extern union {
- pad: [128]u8,
- info: _ksiginfo,
-};
-
-pub const _ksiginfo = extern struct {
+pub const siginfo_t = extern struct {
signo: c_int,
code: c_int,
errno: c_int,
data: extern union {
proc: extern struct {
pid: pid_t,
- uid: uid_t,
- value: sigval,
- utime: clock_t,
- stime: clock_t,
- status: c_int,
+ pdata: extern union {
+ kill: extern struct {
+ uid: uid_t,
+ value: sigval,
+ },
+ cld: extern struct {
+ utime: clock_t,
+ stime: clock_t,
+ status: c_int,
+ },
+ },
},
fault: extern struct {
addr: ?*c_void,
trapno: c_int,
},
- } align(@sizeOf(usize)),
+ __pad: [128 - 3 * @sizeOf(c_int)]u8,
+ },
+};
+
+comptime {
+ if (@sizeOf(usize) == 4)
+ std.debug.assert(@sizeOf(siginfo_t) == 128)
+ else
+ // Take into account the padding between errno and data fields.
+ std.debug.assert(@sizeOf(siginfo_t) == 136);
+}
+
+pub usingnamespace switch (builtin.arch) {
+ .x86_64 => struct {
+ pub const ucontext_t = extern struct {
+ sc_rdi: c_long,
+ sc_rsi: c_long,
+ sc_rdx: c_long,
+ sc_rcx: c_long,
+ sc_r8: c_long,
+ sc_r9: c_long,
+ sc_r10: c_long,
+ sc_r11: c_long,
+ sc_r12: c_long,
+ sc_r13: c_long,
+ sc_r14: c_long,
+ sc_r15: c_long,
+ sc_rbp: c_long,
+ sc_rbx: c_long,
+ sc_rax: c_long,
+ sc_gs: c_long,
+ sc_fs: c_long,
+ sc_es: c_long,
+ sc_ds: c_long,
+ sc_trapno: c_long,
+ sc_err: c_long,
+ sc_rip: c_long,
+ sc_cs: c_long,
+ sc_rflags: c_long,
+ sc_rsp: c_long,
+ sc_ss: c_long,
+
+ sc_fpstate: fxsave64,
+ __sc_unused: c_int,
+ sc_mask: c_int,
+ sc_cookie: c_long,
+ };
+
+ pub const fxsave64 = packed struct {
+ fx_fcw: u16,
+ fx_fsw: u16,
+ fx_ftw: u8,
+ fx_unused1: u8,
+ fx_fop: u16,
+ fx_rip: u64,
+ fx_rdp: u64,
+ fx_mxcsr: u32,
+ fx_mxcsr_mask: u32,
+ fx_st: [8][2]u64,
+ fx_xmm: [16][2]u64,
+ fx_unused3: [96]u8,
+ };
+ },
+ else => struct {},
};
pub const sigset_t = c_uint;
-pub const empty_sigset = sigset_t(0);
+pub const empty_sigset: sigset_t = 0;
pub const EPERM = 1; // Operation not permitted
pub const ENOENT = 2; // No such file or directory
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index b669ed2436..d3fffd231e 100644
--- a/lib/std/os/linux.zig
+++ b/lib/std/os/linux.zig
@@ -745,33 +745,33 @@ pub fn setregid(rgid: gid_t, egid: gid_t) usize {
pub fn getuid() uid_t {
if (@hasField(SYS, "getuid32")) {
- return @as(uid_t, syscall0(.getuid32));
+ return @intCast(uid_t, syscall0(.getuid32));
} else {
- return @as(uid_t, syscall0(.getuid));
+ return @intCast(uid_t, syscall0(.getuid));
}
}
pub fn getgid() gid_t {
if (@hasField(SYS, "getgid32")) {
- return @as(gid_t, syscall0(.getgid32));
+ return @intCast(gid_t, syscall0(.getgid32));
} else {
- return @as(gid_t, syscall0(.getgid));
+ return @intCast(gid_t, syscall0(.getgid));
}
}
pub fn geteuid() uid_t {
if (@hasField(SYS, "geteuid32")) {
- return @as(uid_t, syscall0(.geteuid32));
+ return @intCast(uid_t, syscall0(.geteuid32));
} else {
- return @as(uid_t, syscall0(.geteuid));
+ return @intCast(uid_t, syscall0(.geteuid));
}
}
pub fn getegid() gid_t {
if (@hasField(SYS, "getegid32")) {
- return @as(gid_t, syscall0(.getegid32));
+ return @intCast(gid_t, syscall0(.getegid32));
} else {
- return @as(gid_t, syscall0(.getegid));
+ return @intCast(gid_t, syscall0(.getegid));
}
}
@@ -857,35 +857,42 @@ pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*
return syscall4(.rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8);
}
-pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize {
+pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
assert(sig >= 1);
assert(sig != SIGKILL);
assert(sig != SIGSTOP);
- const restorer_fn = if ((act.flags & SA_SIGINFO) != 0) restore_rt else restore;
- var ksa = k_sigaction{
- .sigaction = act.sigaction,
- .flags = act.flags | SA_RESTORER,
- .mask = undefined,
- .restorer = @ptrCast(fn () callconv(.C) void, restorer_fn),
- };
- var ksa_old: k_sigaction = undefined;
- const ksa_mask_size = @sizeOf(@TypeOf(ksa_old.mask));
- @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), ksa_mask_size);
+ var ksa: k_sigaction = undefined;
+ var oldksa: k_sigaction = undefined;
+ const mask_size = @sizeOf(@TypeOf(ksa.mask));
+
+ if (act) |new| {
+ const restorer_fn = if ((new.flags & SA_SIGINFO) != 0) restore_rt else restore;
+ ksa = k_sigaction{
+ .handler = new.handler.handler,
+ .flags = new.flags | SA_RESTORER,
+ .mask = undefined,
+ .restorer = @ptrCast(fn () callconv(.C) void, restorer_fn),
+ };
+ @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &new.mask), mask_size);
+ }
+
+ const ksa_arg = if (act != null) @ptrToInt(&ksa) else 0;
+ const oldksa_arg = if (oact != null) @ptrToInt(&oldksa) else 0;
+
const result = switch (builtin.arch) {
// The sparc version of rt_sigaction needs the restorer function to be passed as an argument too.
- .sparc, .sparcv9 => syscall5(.rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @ptrToInt(ksa.restorer), ksa_mask_size),
- else => syscall4(.rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), ksa_mask_size),
+ .sparc, .sparcv9 => syscall5(.rt_sigaction, sig, ksa_arg, oldksa_arg, @ptrToInt(ksa.restorer), mask_size),
+ else => syscall4(.rt_sigaction, sig, ksa_arg, oldksa_arg, mask_size),
};
- const err = getErrno(result);
- if (err != 0) {
- return result;
- }
+ if (getErrno(result) != 0) return result;
+
if (oact) |old| {
- old.sigaction = ksa_old.sigaction;
- old.flags = @truncate(u32, ksa_old.flags);
- @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), ksa_mask_size);
+ old.handler.handler = oldksa.handler;
+ old.flags = @truncate(c_uint, oldksa.flags);
+ @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &oldksa.mask), mask_size);
}
+
return 0;
}
@@ -1344,6 +1351,10 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit,
);
}
+pub fn madvise(address: [*]u8, len: usize, advice: u32) usize {
+ return syscall3(.madvise, @ptrToInt(address), len, advice);
+}
+
test "" {
if (builtin.os.tag == .linux) {
_ = @import("linux/test.zig");
diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig
index 7b3840288a..0077fbcee4 100644
--- a/lib/std/os/linux/test.zig
+++ b/lib/std/os/linux/test.zig
@@ -9,6 +9,7 @@ const linux = std.os.linux;
const mem = std.mem;
const elf = std.elf;
const expect = std.testing.expect;
+const expectEqual = std.testing.expectEqual;
const fs = std.fs;
test "fallocate" {
@@ -99,3 +100,11 @@ test "statx" {
expect(@bitCast(u64, @as(i64, stat_buf.blksize)) == statx_buf.blksize);
expect(@bitCast(u64, @as(i64, stat_buf.blocks)) == statx_buf.blocks);
}
+
+test "user and group ids" {
+ if (builtin.link_libc) return error.SkipZigTest;
+ expectEqual(linux.getauxval(elf.AT_UID), linux.getuid());
+ expectEqual(linux.getauxval(elf.AT_GID), linux.getgid());
+ expectEqual(linux.getauxval(elf.AT_EUID), linux.geteuid());
+ expectEqual(linux.getauxval(elf.AT_EGID), linux.getegid());
+}
diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig
index f6c339bc2c..6d61e60e95 100644
--- a/lib/std/os/linux/tls.zig
+++ b/lib/std/os/linux/tls.zig
@@ -327,32 +327,43 @@ pub fn prepareTLS(area: []u8) usize {
if (tls_tp_points_past_tcb) tls_image.data_offset else tls_image.tcb_offset;
}
-var main_thread_tls_buffer: [256]u8 = undefined;
+// The main motivation for the size chosen here is this is how much ends up being
+// requested for the thread local variables of the std.crypto.random implementation.
+// I'm not sure why it ends up being so much; the struct itself is only 64 bytes.
+// I think it has to do with being page aligned and LLVM or LLD is not smart enough
+// to lay out the TLS data in a space conserving way. Anyway I think it's fine
+// because it's less than 3 pages of memory, and putting it in the ELF like this
+// is equivalent to moving the mmap call below into the kernel, avoiding syscall
+// overhead.
+var main_thread_tls_buffer: [0x2100]u8 align(mem.page_size) = undefined;
pub fn initStaticTLS() void {
initTLS();
- const alloc_tls_area: []u8 = blk: {
- const full_alloc_size = tls_image.alloc_size + tls_image.alloc_align - 1;
-
+ const tls_area = blk: {
// Fast path for the common case where the TLS data is really small,
- // avoid an allocation and use our local buffer
- if (full_alloc_size < main_thread_tls_buffer.len)
- break :blk main_thread_tls_buffer[0..];
+ // avoid an allocation and use our local buffer.
+ if (tls_image.alloc_align <= mem.page_size and
+ tls_image.alloc_size <= main_thread_tls_buffer.len)
+ {
+ break :blk main_thread_tls_buffer[0..tls_image.alloc_size];
+ }
- break :blk os.mmap(
+ const alloc_tls_area = os.mmap(
null,
- full_alloc_size,
+ tls_image.alloc_size + tls_image.alloc_align - 1,
os.PROT_READ | os.PROT_WRITE,
os.MAP_PRIVATE | os.MAP_ANONYMOUS,
-1,
0,
) catch os.abort();
- };
- // Make sure the slice is correctly aligned
- const start = @ptrToInt(alloc_tls_area.ptr) & (tls_image.alloc_align - 1);
- const tls_area = alloc_tls_area[start .. start + tls_image.alloc_size];
+ // Make sure the slice is correctly aligned.
+ const begin_addr = @ptrToInt(alloc_tls_area.ptr);
+ const begin_aligned_addr = mem.alignForward(begin_addr, tls_image.alloc_align);
+ const start = begin_aligned_addr - begin_addr;
+ break :blk alloc_tls_area[start .. start + tls_image.alloc_size];
+ };
const tp_value = prepareTLS(tls_area);
setThreadPointer(tp_value);
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
index 81f9922d13..8ad172679b 100644
--- a/lib/std/os/test.zig
+++ b/lib/std/os/test.zig
@@ -646,3 +646,41 @@ test "shutdown socket" {
};
os.closeSocket(sock);
}
+
+var signal_test_failed = true;
+
+test "sigaction" {
+ if (builtin.os.tag == .wasi or builtin.os.tag == .windows)
+ return error.SkipZigTest;
+
+ // https://github.com/ziglang/zig/issues/7427
+ if (builtin.os.tag == .linux and builtin.arch == .i386)
+ return error.SkipZigTest;
+
+ const S = struct {
+ fn handler(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) void {
+ // Check that we received the correct signal.
+ if (sig == os.SIGUSR1 and sig == info.signo)
+ signal_test_failed = false;
+ }
+ };
+
+ var sa = os.Sigaction{
+ .handler = .{ .sigaction = S.handler },
+ .mask = os.empty_sigset,
+ .flags = os.SA_SIGINFO | os.SA_RESETHAND,
+ };
+ var old_sa: os.Sigaction = undefined;
+ // Install the new signal handler.
+ os.sigaction(os.SIGUSR1, &sa, null);
+ // Check that we can read it back correctly.
+ os.sigaction(os.SIGUSR1, null, &old_sa);
+ testing.expectEqual(S.handler, old_sa.handler.sigaction.?);
+ testing.expect((old_sa.flags & os.SA_SIGINFO) != 0);
+ // Invoke the handler.
+ try os.raise(os.SIGUSR1);
+ testing.expect(signal_test_failed == false);
+ // Check if the handler has been correctly reset to SIG_DFL
+ os.sigaction(os.SIGUSR1, null, &old_sa);
+ testing.expectEqual(os.SIG_DFL, old_sa.handler.sigaction);
+}
diff --git a/lib/std/progress.zig b/lib/std/progress.zig
deleted file mode 100644
index 82f2801fa1..0000000000
--- a/lib/std/progress.zig
+++ /dev/null
@@ -1,310 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2020 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("std");
-const windows = std.os.windows;
-const testing = std.testing;
-const assert = std.debug.assert;
-
-/// This API is non-allocating and non-fallible. The tradeoff is that users of
-/// this API must provide the storage for each `Progress.Node`.
-/// Initialize the struct directly, overriding these fields as desired:
-/// * `refresh_rate_ms`
-/// * `initial_delay_ms`
-pub const Progress = struct {
- /// `null` if the current node (and its children) should
- /// not print on update()
- terminal: ?std.fs.File = undefined,
-
- /// Whether the terminal supports ANSI escape codes.
- supports_ansi_escape_codes: bool = false,
-
- root: Node = undefined,
-
- /// Keeps track of how much time has passed since the beginning.
- /// Used to compare with `initial_delay_ms` and `refresh_rate_ms`.
- timer: std.time.Timer = undefined,
-
- /// When the previous refresh was written to the terminal.
- /// Used to compare with `refresh_rate_ms`.
- prev_refresh_timestamp: u64 = undefined,
-
- /// This buffer represents the maximum number of bytes written to the terminal
- /// with each refresh.
- output_buffer: [100]u8 = undefined,
-
- /// How many nanoseconds between writing updates to the terminal.
- refresh_rate_ns: u64 = 50 * std.time.ns_per_ms,
-
- /// How many nanoseconds to keep the output hidden
- initial_delay_ns: u64 = 500 * std.time.ns_per_ms,
-
- done: bool = true,
-
- /// Keeps track of how many columns in the terminal have been output, so that
- /// we can move the cursor back later.
- columns_written: usize = undefined,
-
- /// Represents one unit of progress. Each node can have children nodes, or
- /// one can use integers with `update`.
- pub const Node = struct {
- context: *Progress,
- parent: ?*Node,
- completed_items: usize,
- name: []const u8,
- recently_updated_child: ?*Node = null,
-
- /// This field may be updated freely.
- estimated_total_items: ?usize,
-
- /// Create a new child progress node.
- /// Call `Node.end` when done.
- /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
- /// API to set `self.parent.recently_updated_child` with the return value.
- /// Until that is fixed you probably want to call `activate` on the return value.
- pub fn start(self: *Node, name: []const u8, estimated_total_items: ?usize) Node {
- return Node{
- .context = self.context,
- .parent = self,
- .completed_items = 0,
- .name = name,
- .estimated_total_items = estimated_total_items,
- };
- }
-
- /// This is the same as calling `start` and then `end` on the returned `Node`.
- pub fn completeOne(self: *Node) void {
- if (self.parent) |parent| parent.recently_updated_child = self;
- self.completed_items += 1;
- self.context.maybeRefresh();
- }
-
- pub fn end(self: *Node) void {
- self.context.maybeRefresh();
- if (self.parent) |parent| {
- if (parent.recently_updated_child) |parent_child| {
- if (parent_child == self) {
- parent.recently_updated_child = null;
- }
- }
- parent.completeOne();
- } else {
- self.context.done = true;
- self.context.refresh();
- }
- }
-
- /// Tell the parent node that this node is actively being worked on.
- pub fn activate(self: *Node) void {
- if (self.parent) |parent| parent.recently_updated_child = self;
- }
- };
-
- /// Create a new progress node.
- /// Call `Node.end` when done.
- /// TODO solve https://github.com/ziglang/zig/issues/2765 and then change this
- /// API to return Progress rather than accept it as a parameter.
- pub fn start(self: *Progress, name: []const u8, estimated_total_items: ?usize) !*Node {
- const stderr = std.io.getStdErr();
- self.terminal = null;
- if (stderr.supportsAnsiEscapeCodes()) {
- self.terminal = stderr;
- self.supports_ansi_escape_codes = true;
- } else if (std.builtin.os.tag == .windows and stderr.isTty()) {
- self.terminal = stderr;
- }
- self.root = Node{
- .context = self,
- .parent = null,
- .completed_items = 0,
- .name = name,
- .estimated_total_items = estimated_total_items,
- };
- self.columns_written = 0;
- self.prev_refresh_timestamp = 0;
- self.timer = try std.time.Timer.start();
- self.done = false;
- return &self.root;
- }
-
- /// Updates the terminal if enough time has passed since last update.
- pub fn maybeRefresh(self: *Progress) void {
- const now = self.timer.read();
- if (now < self.initial_delay_ns) return;
- if (now - self.prev_refresh_timestamp < self.refresh_rate_ns) return;
- self.refresh();
- }
-
- /// Updates the terminal and resets `self.next_refresh_timestamp`.
- pub fn refresh(self: *Progress) void {
- const file = self.terminal orelse return;
-
- const prev_columns_written = self.columns_written;
- var end: usize = 0;
- if (self.columns_written > 0) {
- // restore the cursor position by moving the cursor
- // `columns_written` cells to the left, then clear the rest of the
- // line
- if (self.supports_ansi_escape_codes) {
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[{}D", .{self.columns_written}) catch unreachable).len;
- end += (std.fmt.bufPrint(self.output_buffer[end..], "\x1b[0K", .{}) catch unreachable).len;
- } else if (std.builtin.os.tag == .windows) winapi: {
- var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
- if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE)
- unreachable;
-
- var cursor_pos = windows.COORD{
- .X = info.dwCursorPosition.X - @intCast(windows.SHORT, self.columns_written),
- .Y = info.dwCursorPosition.Y,
- };
-
- if (cursor_pos.X < 0)
- cursor_pos.X = 0;
-
- const fill_chars = @intCast(windows.DWORD, info.dwSize.X - cursor_pos.X);
-
- var written: windows.DWORD = undefined;
- if (windows.kernel32.FillConsoleOutputAttribute(
- file.handle,
- info.wAttributes,
- fill_chars,
- cursor_pos,
- &written,
- ) != windows.TRUE) {
- // Stop trying to write to this file.
- self.terminal = null;
- break :winapi;
- }
- if (windows.kernel32.FillConsoleOutputCharacterA(
- file.handle,
- ' ',
- fill_chars,
- cursor_pos,
- &written,
- ) != windows.TRUE) unreachable;
-
- if (windows.kernel32.SetConsoleCursorPosition(file.handle, cursor_pos) != windows.TRUE)
- unreachable;
- } else unreachable;
-
- self.columns_written = 0;
- }
-
- if (!self.done) {
- var need_ellipse = false;
- var maybe_node: ?*Node = &self.root;
- while (maybe_node) |node| {
- if (need_ellipse) {
- self.bufWrite(&end, "... ", .{});
- }
- need_ellipse = false;
- if (node.name.len != 0 or node.estimated_total_items != null) {
- if (node.name.len != 0) {
- self.bufWrite(&end, "{}", .{node.name});
- need_ellipse = true;
- }
- if (node.estimated_total_items) |total| {
- if (need_ellipse) self.bufWrite(&end, " ", .{});
- self.bufWrite(&end, "[{}/{}] ", .{ node.completed_items + 1, total });
- need_ellipse = false;
- } else if (node.completed_items != 0) {
- if (need_ellipse) self.bufWrite(&end, " ", .{});
- self.bufWrite(&end, "[{}] ", .{node.completed_items + 1});
- need_ellipse = false;
- }
- }
- maybe_node = node.recently_updated_child;
- }
- if (need_ellipse) {
- self.bufWrite(&end, "... ", .{});
- }
- }
-
- _ = file.write(self.output_buffer[0..end]) catch |e| {
- // Stop trying to write to this file once it errors.
- self.terminal = null;
- };
- self.prev_refresh_timestamp = self.timer.read();
- }
-
- pub fn log(self: *Progress, comptime format: []const u8, args: anytype) void {
- const file = self.terminal orelse return;
- self.refresh();
- file.outStream().print(format, args) catch {
- self.terminal = null;
- return;
- };
- self.columns_written = 0;
- }
-
- fn bufWrite(self: *Progress, end: *usize, comptime format: []const u8, args: anytype) void {
- if (std.fmt.bufPrint(self.output_buffer[end.*..], format, args)) |written| {
- const amt = written.len;
- end.* += amt;
- self.columns_written += amt;
- } else |err| switch (err) {
- error.NoSpaceLeft => {
- self.columns_written += self.output_buffer.len - end.*;
- end.* = self.output_buffer.len;
- },
- }
- const bytes_needed_for_esc_codes_at_end = if (std.builtin.os.tag == .windows) 0 else 11;
- const max_end = self.output_buffer.len - bytes_needed_for_esc_codes_at_end;
- if (end.* > max_end) {
- const suffix = "... ";
- self.columns_written = self.columns_written - (end.* - max_end) + suffix.len;
- std.mem.copy(u8, self.output_buffer[max_end..], suffix);
- end.* = max_end + suffix.len;
- }
- }
-};
-
-test "basic functionality" {
- var disable = true;
- if (disable) {
- // This test is disabled because it uses time.sleep() and is therefore slow. It also
- // prints bogus progress data to stderr.
- return error.SkipZigTest;
- }
- var progress = Progress{};
- const root_node = try progress.start("", 100);
- defer root_node.end();
-
- const sub_task_names = [_][]const u8{
- "reticulating splines",
- "adjusting shoes",
- "climbing towers",
- "pouring juice",
- };
- var next_sub_task: usize = 0;
-
- var i: usize = 0;
- while (i < 100) : (i += 1) {
- var node = root_node.start(sub_task_names[next_sub_task], 5);
- node.activate();
- next_sub_task = (next_sub_task + 1) % sub_task_names.len;
-
- node.completeOne();
- std.time.sleep(5 * std.time.ns_per_ms);
- node.completeOne();
- node.completeOne();
- std.time.sleep(5 * std.time.ns_per_ms);
- node.completeOne();
- node.completeOne();
- std.time.sleep(5 * std.time.ns_per_ms);
-
- node.end();
-
- std.time.sleep(5 * std.time.ns_per_ms);
- }
- {
- var node = root_node.start("this is a really long name designed to activate the truncation code. let's find out if it works", null);
- node.activate();
- std.time.sleep(10 * std.time.ns_per_ms);
- progress.refresh();
- std.time.sleep(10 * std.time.ns_per_ms);
- node.end();
- }
-}
diff --git a/lib/std/rand.zig b/lib/std/rand.zig
index a201968a5f..681c67f22d 100644
--- a/lib/std/rand.zig
+++ b/lib/std/rand.zig
@@ -4,19 +4,11 @@
// The MIT license requires this copyright notice to be included in all copies
// and substantial portions of the software.
-//! The engines provided here should be initialized from an external source. For now, randomBytes
-//! from the crypto package is the most suitable. Be sure to use a CSPRNG when required, otherwise using
-//! a normal PRNG will be faster and use substantially less stack space.
-//!
-//! ```
-//! var buf: [8]u8 = undefined;
-//! try std.crypto.randomBytes(buf[0..]);
-//! const seed = mem.readIntLittle(u64, buf[0..8]);
-//!
-//! var r = DefaultPrng.init(seed);
-//!
-//! const s = r.random.int(u64);
-//! ```
+//! The engines provided here should be initialized from an external source.
+//! For a thread-local cryptographically secure pseudo random number generator,
+//! use `std.crypto.random`.
+//! Be sure to use a CSPRNG when required, otherwise using a normal PRNG will
+//! be faster and use substantially less stack space.
//!
//! TODO(tiehuis): Benchmark these against other reference implementations.
@@ -36,6 +28,12 @@ pub const DefaultPrng = Xoroshiro128;
/// Cryptographically secure random numbers.
pub const DefaultCsprng = Gimli;
+pub const Isaac64 = @import("rand/Isaac64.zig");
+pub const Gimli = @import("rand/Gimli.zig");
+pub const Pcg = @import("rand/Pcg.zig");
+pub const Xoroshiro128 = @import("rand/Xoroshiro128.zig");
+pub const Sfc64 = @import("rand/Sfc64.zig");
+
pub const Random = struct {
fillFn: fn (r: *Random, buf: []u8) void,
@@ -491,7 +489,7 @@ test "Random Biased" {
//
// The number of cycles is thus limited to 64-bits regardless of the engine, but this
// is still plenty for practical purposes.
-const SplitMix64 = struct {
+pub const SplitMix64 = struct {
s: u64,
pub fn init(seed: u64) SplitMix64 {
@@ -525,557 +523,6 @@ test "splitmix64 sequence" {
}
}
-// PCG32 - http://www.pcg-random.org/
-//
-// PRNG
-pub const Pcg = struct {
- const default_multiplier = 6364136223846793005;
-
- random: Random,
-
- s: u64,
- i: u64,
-
- pub fn init(init_s: u64) Pcg {
- var pcg = Pcg{
- .random = Random{ .fillFn = fill },
- .s = undefined,
- .i = undefined,
- };
-
- pcg.seed(init_s);
- return pcg;
- }
-
- fn next(self: *Pcg) u32 {
- const l = self.s;
- self.s = l *% default_multiplier +% (self.i | 1);
-
- const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
- const rot = @intCast(u32, l >> 59);
-
- return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31));
- }
-
- fn seed(self: *Pcg, init_s: u64) void {
- // Pcg requires 128-bits of seed.
- var gen = SplitMix64.init(init_s);
- self.seedTwo(gen.next(), gen.next());
- }
-
- fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void {
- self.s = 0;
- self.i = (init_s << 1) | 1;
- self.s = self.s *% default_multiplier +% self.i;
- self.s +%= init_i;
- self.s = self.s *% default_multiplier +% self.i;
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Pcg, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Complete 4 byte segments.
- while (i < aligned_len) : (i += 4) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 4) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Remaining. (cuts the stream)
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 4;
- }
- }
- }
-};
-
-test "pcg sequence" {
- var r = Pcg.init(0);
- const s0: u64 = 0x9394bf54ce5d79de;
- const s1: u64 = 0x84e9c579ef59bbf7;
- r.seedTwo(s0, s1);
-
- const seq = [_]u32{
- 2881561918,
- 3063928540,
- 1199791034,
- 2487695858,
- 1479648952,
- 3247963454,
- };
-
- for (seq) |s| {
- expect(s == r.next());
- }
-}
-
-// Xoroshiro128+ - http://xoroshiro.di.unimi.it/
-//
-// PRNG
-pub const Xoroshiro128 = struct {
- random: Random,
-
- s: [2]u64,
-
- pub fn init(init_s: u64) Xoroshiro128 {
- var x = Xoroshiro128{
- .random = Random{ .fillFn = fill },
- .s = undefined,
- };
-
- x.seed(init_s);
- return x;
- }
-
- fn next(self: *Xoroshiro128) u64 {
- const s0 = self.s[0];
- var s1 = self.s[1];
- const r = s0 +% s1;
-
- s1 ^= s0;
- self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
- self.s[1] = math.rotl(u64, s1, @as(u8, 36));
-
- return r;
- }
-
- // Skip 2^64 places ahead in the sequence
- fn jump(self: *Xoroshiro128) void {
- var s0: u64 = 0;
- var s1: u64 = 0;
-
- const table = [_]u64{
- 0xbeac0467eba5facb,
- 0xd86b048b86aa9922,
- };
-
- inline for (table) |entry| {
- var b: usize = 0;
- while (b < 64) : (b += 1) {
- if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) {
- s0 ^= self.s[0];
- s1 ^= self.s[1];
- }
- _ = self.next();
- }
- }
-
- self.s[0] = s0;
- self.s[1] = s1;
- }
-
- pub fn seed(self: *Xoroshiro128, init_s: u64) void {
- // Xoroshiro requires 128-bits of seed.
- var gen = SplitMix64.init(init_s);
-
- self.s[0] = gen.next();
- self.s[1] = gen.next();
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Xoroshiro128, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Complete 8 byte segments.
- while (i < aligned_len) : (i += 8) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 8) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Remaining. (cuts the stream)
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 8;
- }
- }
- }
-};
-
-test "xoroshiro sequence" {
- var r = Xoroshiro128.init(0);
- r.s[0] = 0xaeecf86f7878dd75;
- r.s[1] = 0x01cd153642e72622;
-
- const seq1 = [_]u64{
- 0xb0ba0da5bb600397,
- 0x18a08afde614dccc,
- 0xa2635b956a31b929,
- 0xabe633c971efa045,
- 0x9ac19f9706ca3cac,
- 0xf62b426578c1e3fb,
- };
-
- for (seq1) |s| {
- expect(s == r.next());
- }
-
- r.jump();
-
- const seq2 = [_]u64{
- 0x95344a13556d3e22,
- 0xb4fb32dafa4d00df,
- 0xb2011d9ccdcfe2dd,
- 0x05679a9b2119b908,
- 0xa860a1da7c9cd8a0,
- 0x658a96efe3f86550,
- };
-
- for (seq2) |s| {
- expect(s == r.next());
- }
-}
-
-// Gimli
-//
-// CSPRNG
-pub const Gimli = struct {
- random: Random,
- state: std.crypto.core.Gimli,
-
- pub const secret_seed_length = 32;
-
- /// The seed must be uniform, secret and `secret_seed_length` bytes long.
- /// It can be generated using `std.crypto.randomBytes()`.
- pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
- var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
- mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
- mem.set(u8, initial_state[secret_seed_length..], 0);
- var self = Gimli{
- .random = Random{ .fillFn = fill },
- .state = std.crypto.core.Gimli.init(initial_state),
- };
- return self;
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Gimli, "random", r);
-
- if (buf.len != 0) {
- self.state.squeeze(buf);
- } else {
- self.state.permute();
- }
- mem.set(u8, self.state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
- }
-};
-
-// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
-//
-// Follows the general idea of the implementation from here with a few shortcuts.
-// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
-pub const Isaac64 = struct {
- random: Random,
-
- r: [256]u64,
- m: [256]u64,
- a: u64,
- b: u64,
- c: u64,
- i: usize,
-
- pub fn init(init_s: u64) Isaac64 {
- var isaac = Isaac64{
- .random = Random{ .fillFn = fill },
- .r = undefined,
- .m = undefined,
- .a = undefined,
- .b = undefined,
- .c = undefined,
- .i = undefined,
- };
-
- // seed == 0 => same result as the unseeded reference implementation
- isaac.seed(init_s, 1);
- return isaac;
- }
-
- fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
- const x = self.m[base + m1];
- self.a = mix +% self.m[base + m2];
-
- const y = self.a +% self.b +% self.m[@intCast(usize, (x >> 3) % self.m.len)];
- self.m[base + m1] = y;
-
- self.b = x +% self.m[@intCast(usize, (y >> 11) % self.m.len)];
- self.r[self.r.len - 1 - base - m1] = self.b;
- }
-
- fn refill(self: *Isaac64) void {
- const midpoint = self.r.len / 2;
-
- self.c +%= 1;
- self.b +%= self.c;
-
- {
- var i: usize = 0;
- while (i < midpoint) : (i += 4) {
- self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
- self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint);
- self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint);
- self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint);
- }
- }
-
- {
- var i: usize = 0;
- while (i < midpoint) : (i += 4) {
- self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
- self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0);
- self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0);
- self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0);
- }
- }
-
- self.i = 0;
- }
-
- fn next(self: *Isaac64) u64 {
- if (self.i >= self.r.len) {
- self.refill();
- }
-
- const value = self.r[self.i];
- self.i += 1;
- return value;
- }
-
- fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
- // We ignore the multi-pass requirement since we don't currently expose full access to
- // seeding the self.m array completely.
- mem.set(u64, self.m[0..], 0);
- self.m[0] = init_s;
-
- // prescrambled golden ratio constants
- var a = [_]u64{
- 0x647c4677a2884b7c,
- 0xb9f8b322c73ac862,
- 0x8c0ea5053d4712a0,
- 0xb29b2e824a595524,
- 0x82f053db8355e0ce,
- 0x48fe4a0fa5a09315,
- 0xae985bf2cbfc89ed,
- 0x98f5704f6c44c0ab,
- };
-
- comptime var i: usize = 0;
- inline while (i < rounds) : (i += 1) {
- var j: usize = 0;
- while (j < self.m.len) : (j += 8) {
- comptime var x1: usize = 0;
- inline while (x1 < 8) : (x1 += 1) {
- a[x1] +%= self.m[j + x1];
- }
-
- a[0] -%= a[4];
- a[5] ^= a[7] >> 9;
- a[7] +%= a[0];
- a[1] -%= a[5];
- a[6] ^= a[0] << 9;
- a[0] +%= a[1];
- a[2] -%= a[6];
- a[7] ^= a[1] >> 23;
- a[1] +%= a[2];
- a[3] -%= a[7];
- a[0] ^= a[2] << 15;
- a[2] +%= a[3];
- a[4] -%= a[0];
- a[1] ^= a[3] >> 14;
- a[3] +%= a[4];
- a[5] -%= a[1];
- a[2] ^= a[4] << 20;
- a[4] +%= a[5];
- a[6] -%= a[2];
- a[3] ^= a[5] >> 17;
- a[5] +%= a[6];
- a[7] -%= a[3];
- a[4] ^= a[6] << 14;
- a[6] +%= a[7];
-
- comptime var x2: usize = 0;
- inline while (x2 < 8) : (x2 += 1) {
- self.m[j + x2] = a[x2];
- }
- }
- }
-
- mem.set(u64, self.r[0..], 0);
- self.a = 0;
- self.b = 0;
- self.c = 0;
- self.i = self.r.len; // trigger refill on first value
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Isaac64, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Fill complete 64-byte segments
- while (i < aligned_len) : (i += 8) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 8) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Fill trailing, ignoring excess (cut the stream).
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 8;
- }
- }
- }
-};
-
-test "isaac64 sequence" {
- var r = Isaac64.init(0);
-
- // from reference implementation
- const seq = [_]u64{
- 0xf67dfba498e4937c,
- 0x84a5066a9204f380,
- 0xfee34bd5f5514dbb,
- 0x4d1664739b8f80d6,
- 0x8607459ab52a14aa,
- 0x0e78bc5a98529e49,
- 0xfe5332822ad13777,
- 0x556c27525e33d01a,
- 0x08643ca615f3149f,
- 0xd0771faf3cb04714,
- 0x30e86f68a37b008d,
- 0x3074ebc0488a3adf,
- 0x270645ea7a2790bc,
- 0x5601a0a8d3763c6a,
- 0x2f83071f53f325dd,
- 0xb9090f3d42d2d2ea,
- };
-
- for (seq) |s| {
- expect(s == r.next());
- }
-}
-
-/// Sfc64 pseudo-random number generator from Practically Random.
-/// Fastest engine of pracrand and smallest footprint.
-/// See http://pracrand.sourceforge.net/
-pub const Sfc64 = struct {
- random: Random,
-
- a: u64 = undefined,
- b: u64 = undefined,
- c: u64 = undefined,
- counter: u64 = undefined,
-
- const Rotation = 24;
- const RightShift = 11;
- const LeftShift = 3;
-
- pub fn init(init_s: u64) Sfc64 {
- var x = Sfc64{
- .random = Random{ .fillFn = fill },
- };
-
- x.seed(init_s);
- return x;
- }
-
- fn next(self: *Sfc64) u64 {
- const tmp = self.a +% self.b +% self.counter;
- self.counter += 1;
- self.a = self.b ^ (self.b >> RightShift);
- self.b = self.c +% (self.c << LeftShift);
- self.c = math.rotl(u64, self.c, Rotation) +% tmp;
- return tmp;
- }
-
- fn seed(self: *Sfc64, init_s: u64) void {
- self.a = init_s;
- self.b = init_s;
- self.c = init_s;
- self.counter = 1;
- var i: u32 = 0;
- while (i < 12) : (i += 1) {
- _ = self.next();
- }
- }
-
- fn fill(r: *Random, buf: []u8) void {
- const self = @fieldParentPtr(Sfc64, "random", r);
-
- var i: usize = 0;
- const aligned_len = buf.len - (buf.len & 7);
-
- // Complete 8 byte segments.
- while (i < aligned_len) : (i += 8) {
- var n = self.next();
- comptime var j: usize = 0;
- inline while (j < 8) : (j += 1) {
- buf[i + j] = @truncate(u8, n);
- n >>= 8;
- }
- }
-
- // Remaining. (cuts the stream)
- if (i != buf.len) {
- var n = self.next();
- while (i < buf.len) : (i += 1) {
- buf[i] = @truncate(u8, n);
- n >>= 8;
- }
- }
- }
-};
-
-test "Sfc64 sequence" {
- // Unfortunately there does not seem to be an official test sequence.
- var r = Sfc64.init(0);
-
- const seq = [_]u64{
- 0x3acfa029e3cc6041,
- 0xf5b6515bf2ee419c,
- 0x1259635894a29b61,
- 0xb6ae75395f8ebd6,
- 0x225622285ce302e2,
- 0x520d28611395cb21,
- 0xdb909c818901599d,
- 0x8ffd195365216f57,
- 0xe8c4ad5e258ac04a,
- 0x8f8ef2c89fdb63ca,
- 0xf9865b01d98d8e2f,
- 0x46555871a65d08ba,
- 0x66868677c6298fcd,
- 0x2ce15a7e6329f57d,
- 0xb2f1833ca91ca79,
- 0x4b0890ac9bf453ca,
- };
-
- for (seq) |s| {
- expectEqual(s, r.next());
- }
-}
-
// Actual Random helper function tests, pcg engine is assumed correct.
test "Random float" {
var prng = DefaultPrng.init(0);
@@ -1147,7 +594,7 @@ fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void {
test "CSPRNG" {
var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
- try std.crypto.randomBytes(&secret_seed);
+ std.crypto.random.bytes(&secret_seed);
var csprng = DefaultCsprng.init(secret_seed);
const a = csprng.random.int(u64);
const b = csprng.random.int(u64);
diff --git a/lib/std/rand/Gimli.zig b/lib/std/rand/Gimli.zig
new file mode 100644
index 0000000000..32331e7153
--- /dev/null
+++ b/lib/std/rand/Gimli.zig
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! CSPRNG
+
+const std = @import("std");
+const Random = std.rand.Random;
+const mem = std.mem;
+const Gimli = @This();
+
+random: Random,
+state: std.crypto.core.Gimli,
+
+pub const secret_seed_length = 32;
+
+/// The seed must be uniform, secret and `secret_seed_length` bytes long.
+pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
+ var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
+ mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
+ mem.set(u8, initial_state[secret_seed_length..], 0);
+ var self = Gimli{
+ .random = Random{ .fillFn = fill },
+ .state = std.crypto.core.Gimli.init(initial_state),
+ };
+ return self;
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Gimli, "random", r);
+
+ if (buf.len != 0) {
+ self.state.squeeze(buf);
+ } else {
+ self.state.permute();
+ }
+ mem.set(u8, self.state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
+}
diff --git a/lib/std/rand/Isaac64.zig b/lib/std/rand/Isaac64.zig
new file mode 100644
index 0000000000..a079f19b9e
--- /dev/null
+++ b/lib/std/rand/Isaac64.zig
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
+//!
+//! Follows the general idea of the implementation from here with a few shortcuts.
+//! https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
+
+const std = @import("std");
+const Random = std.rand.Random;
+const mem = std.mem;
+const Isaac64 = @This();
+
+random: Random,
+
+r: [256]u64,
+m: [256]u64,
+a: u64,
+b: u64,
+c: u64,
+i: usize,
+
+pub fn init(init_s: u64) Isaac64 {
+ var isaac = Isaac64{
+ .random = Random{ .fillFn = fill },
+ .r = undefined,
+ .m = undefined,
+ .a = undefined,
+ .b = undefined,
+ .c = undefined,
+ .i = undefined,
+ };
+
+ // seed == 0 => same result as the unseeded reference implementation
+ isaac.seed(init_s, 1);
+ return isaac;
+}
+
+fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
+ const x = self.m[base + m1];
+ self.a = mix +% self.m[base + m2];
+
+ const y = self.a +% self.b +% self.m[@intCast(usize, (x >> 3) % self.m.len)];
+ self.m[base + m1] = y;
+
+ self.b = x +% self.m[@intCast(usize, (y >> 11) % self.m.len)];
+ self.r[self.r.len - 1 - base - m1] = self.b;
+}
+
+fn refill(self: *Isaac64) void {
+ const midpoint = self.r.len / 2;
+
+ self.c +%= 1;
+ self.b +%= self.c;
+
+ {
+ var i: usize = 0;
+ while (i < midpoint) : (i += 4) {
+ self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
+ self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint);
+ self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint);
+ self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint);
+ }
+ }
+
+ {
+ var i: usize = 0;
+ while (i < midpoint) : (i += 4) {
+ self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
+ self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0);
+ self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0);
+ self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0);
+ }
+ }
+
+ self.i = 0;
+}
+
+fn next(self: *Isaac64) u64 {
+ if (self.i >= self.r.len) {
+ self.refill();
+ }
+
+ const value = self.r[self.i];
+ self.i += 1;
+ return value;
+}
+
+fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void {
+ // We ignore the multi-pass requirement since we don't currently expose full access to
+ // seeding the self.m array completely.
+ mem.set(u64, self.m[0..], 0);
+ self.m[0] = init_s;
+
+ // prescrambled golden ratio constants
+ var a = [_]u64{
+ 0x647c4677a2884b7c,
+ 0xb9f8b322c73ac862,
+ 0x8c0ea5053d4712a0,
+ 0xb29b2e824a595524,
+ 0x82f053db8355e0ce,
+ 0x48fe4a0fa5a09315,
+ 0xae985bf2cbfc89ed,
+ 0x98f5704f6c44c0ab,
+ };
+
+ comptime var i: usize = 0;
+ inline while (i < rounds) : (i += 1) {
+ var j: usize = 0;
+ while (j < self.m.len) : (j += 8) {
+ comptime var x1: usize = 0;
+ inline while (x1 < 8) : (x1 += 1) {
+ a[x1] +%= self.m[j + x1];
+ }
+
+ a[0] -%= a[4];
+ a[5] ^= a[7] >> 9;
+ a[7] +%= a[0];
+ a[1] -%= a[5];
+ a[6] ^= a[0] << 9;
+ a[0] +%= a[1];
+ a[2] -%= a[6];
+ a[7] ^= a[1] >> 23;
+ a[1] +%= a[2];
+ a[3] -%= a[7];
+ a[0] ^= a[2] << 15;
+ a[2] +%= a[3];
+ a[4] -%= a[0];
+ a[1] ^= a[3] >> 14;
+ a[3] +%= a[4];
+ a[5] -%= a[1];
+ a[2] ^= a[4] << 20;
+ a[4] +%= a[5];
+ a[6] -%= a[2];
+ a[3] ^= a[5] >> 17;
+ a[5] +%= a[6];
+ a[7] -%= a[3];
+ a[4] ^= a[6] << 14;
+ a[6] +%= a[7];
+
+ comptime var x2: usize = 0;
+ inline while (x2 < 8) : (x2 += 1) {
+ self.m[j + x2] = a[x2];
+ }
+ }
+ }
+
+ mem.set(u64, self.r[0..], 0);
+ self.a = 0;
+ self.b = 0;
+ self.c = 0;
+ self.i = self.r.len; // trigger refill on first value
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Isaac64, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Fill complete 64-byte segments
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Fill trailing, ignoring excess (cut the stream).
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+}
+
+test "isaac64 sequence" {
+ var r = Isaac64.init(0);
+
+ // from reference implementation
+ const seq = [_]u64{
+ 0xf67dfba498e4937c,
+ 0x84a5066a9204f380,
+ 0xfee34bd5f5514dbb,
+ 0x4d1664739b8f80d6,
+ 0x8607459ab52a14aa,
+ 0x0e78bc5a98529e49,
+ 0xfe5332822ad13777,
+ 0x556c27525e33d01a,
+ 0x08643ca615f3149f,
+ 0xd0771faf3cb04714,
+ 0x30e86f68a37b008d,
+ 0x3074ebc0488a3adf,
+ 0x270645ea7a2790bc,
+ 0x5601a0a8d3763c6a,
+ 0x2f83071f53f325dd,
+ 0xb9090f3d42d2d2ea,
+ };
+
+ for (seq) |s| {
+ std.testing.expect(s == r.next());
+ }
+}
diff --git a/lib/std/rand/Pcg.zig b/lib/std/rand/Pcg.zig
new file mode 100644
index 0000000000..1bbe8beb63
--- /dev/null
+++ b/lib/std/rand/Pcg.zig
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! PCG32 - http://www.pcg-random.org/
+//!
+//! PRNG
+
+const std = @import("std");
+const Random = std.rand.Random;
+const Pcg = @This();
+
+const default_multiplier = 6364136223846793005;
+
+random: Random,
+
+s: u64,
+i: u64,
+
+pub fn init(init_s: u64) Pcg {
+ var pcg = Pcg{
+ .random = Random{ .fillFn = fill },
+ .s = undefined,
+ .i = undefined,
+ };
+
+ pcg.seed(init_s);
+ return pcg;
+}
+
+fn next(self: *Pcg) u32 {
+ const l = self.s;
+ self.s = l *% default_multiplier +% (self.i | 1);
+
+ const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
+ const rot = @intCast(u32, l >> 59);
+
+ return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31));
+}
+
+fn seed(self: *Pcg, init_s: u64) void {
+ // Pcg requires 128-bits of seed.
+ var gen = std.rand.SplitMix64.init(init_s);
+ self.seedTwo(gen.next(), gen.next());
+}
+
+fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void {
+ self.s = 0;
+ self.i = (init_s << 1) | 1;
+ self.s = self.s *% default_multiplier +% self.i;
+ self.s +%= init_i;
+ self.s = self.s *% default_multiplier +% self.i;
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Pcg, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 4 byte segments.
+ while (i < aligned_len) : (i += 4) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 4) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 4;
+ }
+ }
+}
+
+test "pcg sequence" {
+ var r = Pcg.init(0);
+ const s0: u64 = 0x9394bf54ce5d79de;
+ const s1: u64 = 0x84e9c579ef59bbf7;
+ r.seedTwo(s0, s1);
+
+ const seq = [_]u32{
+ 2881561918,
+ 3063928540,
+ 1199791034,
+ 2487695858,
+ 1479648952,
+ 3247963454,
+ };
+
+ for (seq) |s| {
+ std.testing.expect(s == r.next());
+ }
+}
diff --git a/lib/std/rand/Sfc64.zig b/lib/std/rand/Sfc64.zig
new file mode 100644
index 0000000000..feba5d884c
--- /dev/null
+++ b/lib/std/rand/Sfc64.zig
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Sfc64 pseudo-random number generator from Practically Random.
+//! Fastest engine of pracrand and smallest footprint.
+//! See http://pracrand.sourceforge.net/
+
+const std = @import("std");
+const Random = std.rand.Random;
+const math = std.math;
+const Sfc64 = @This();
+
+random: Random,
+
+a: u64 = undefined,
+b: u64 = undefined,
+c: u64 = undefined,
+counter: u64 = undefined,
+
+const Rotation = 24;
+const RightShift = 11;
+const LeftShift = 3;
+
+pub fn init(init_s: u64) Sfc64 {
+ var x = Sfc64{
+ .random = Random{ .fillFn = fill },
+ };
+
+ x.seed(init_s);
+ return x;
+}
+
+fn next(self: *Sfc64) u64 {
+ const tmp = self.a +% self.b +% self.counter;
+ self.counter += 1;
+ self.a = self.b ^ (self.b >> RightShift);
+ self.b = self.c +% (self.c << LeftShift);
+ self.c = math.rotl(u64, self.c, Rotation) +% tmp;
+ return tmp;
+}
+
+fn seed(self: *Sfc64, init_s: u64) void {
+ self.a = init_s;
+ self.b = init_s;
+ self.c = init_s;
+ self.counter = 1;
+ var i: u32 = 0;
+ while (i < 12) : (i += 1) {
+ _ = self.next();
+ }
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Sfc64, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 8 byte segments.
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+}
+
+test "Sfc64 sequence" {
+ // Unfortunately there does not seem to be an official test sequence.
+ var r = Sfc64.init(0);
+
+ const seq = [_]u64{
+ 0x3acfa029e3cc6041,
+ 0xf5b6515bf2ee419c,
+ 0x1259635894a29b61,
+ 0xb6ae75395f8ebd6,
+ 0x225622285ce302e2,
+ 0x520d28611395cb21,
+ 0xdb909c818901599d,
+ 0x8ffd195365216f57,
+ 0xe8c4ad5e258ac04a,
+ 0x8f8ef2c89fdb63ca,
+ 0xf9865b01d98d8e2f,
+ 0x46555871a65d08ba,
+ 0x66868677c6298fcd,
+ 0x2ce15a7e6329f57d,
+ 0xb2f1833ca91ca79,
+ 0x4b0890ac9bf453ca,
+ };
+
+ for (seq) |s| {
+ std.testing.expectEqual(s, r.next());
+ }
+}
diff --git a/lib/std/rand/Xoroshiro128.zig b/lib/std/rand/Xoroshiro128.zig
new file mode 100644
index 0000000000..2cc9bf9070
--- /dev/null
+++ b/lib/std/rand/Xoroshiro128.zig
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2020 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+
+//! Xoroshiro128+ - http://xoroshiro.di.unimi.it/
+//!
+//! PRNG
+
+const std = @import("std");
+const Random = std.rand.Random;
+const math = std.math;
+const Xoroshiro128 = @This();
+
+random: Random,
+
+s: [2]u64,
+
+pub fn init(init_s: u64) Xoroshiro128 {
+ var x = Xoroshiro128{
+ .random = Random{ .fillFn = fill },
+ .s = undefined,
+ };
+
+ x.seed(init_s);
+ return x;
+}
+
+fn next(self: *Xoroshiro128) u64 {
+ const s0 = self.s[0];
+ var s1 = self.s[1];
+ const r = s0 +% s1;
+
+ s1 ^= s0;
+ self.s[0] = math.rotl(u64, s0, @as(u8, 55)) ^ s1 ^ (s1 << 14);
+ self.s[1] = math.rotl(u64, s1, @as(u8, 36));
+
+ return r;
+}
+
+// Skip 2^64 places ahead in the sequence
+fn jump(self: *Xoroshiro128) void {
+ var s0: u64 = 0;
+ var s1: u64 = 0;
+
+ const table = [_]u64{
+ 0xbeac0467eba5facb,
+ 0xd86b048b86aa9922,
+ };
+
+ inline for (table) |entry| {
+ var b: usize = 0;
+ while (b < 64) : (b += 1) {
+ if ((entry & (@as(u64, 1) << @intCast(u6, b))) != 0) {
+ s0 ^= self.s[0];
+ s1 ^= self.s[1];
+ }
+ _ = self.next();
+ }
+ }
+
+ self.s[0] = s0;
+ self.s[1] = s1;
+}
+
+pub fn seed(self: *Xoroshiro128, init_s: u64) void {
+ // Xoroshiro requires 128-bits of seed.
+ var gen = std.rand.SplitMix64.init(init_s);
+
+ self.s[0] = gen.next();
+ self.s[1] = gen.next();
+}
+
+fn fill(r: *Random, buf: []u8) void {
+ const self = @fieldParentPtr(Xoroshiro128, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 8 byte segments.
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+}
+
+test "xoroshiro sequence" {
+ var r = Xoroshiro128.init(0);
+ r.s[0] = 0xaeecf86f7878dd75;
+ r.s[1] = 0x01cd153642e72622;
+
+ const seq1 = [_]u64{
+ 0xb0ba0da5bb600397,
+ 0x18a08afde614dccc,
+ 0xa2635b956a31b929,
+ 0xabe633c971efa045,
+ 0x9ac19f9706ca3cac,
+ 0xf62b426578c1e3fb,
+ };
+
+ for (seq1) |s| {
+ std.testing.expect(s == r.next());
+ }
+
+ r.jump();
+
+ const seq2 = [_]u64{
+ 0x95344a13556d3e22,
+ 0xb4fb32dafa4d00df,
+ 0xb2011d9ccdcfe2dd,
+ 0x05679a9b2119b908,
+ 0xa860a1da7c9cd8a0,
+ 0x658a96efe3f86550,
+ };
+
+ for (seq2) |s| {
+ std.testing.expect(s == r.next());
+ }
+}
diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig
index 449b70d6b0..84aeb6aebb 100644
--- a/lib/std/special/c.zig
+++ b/lib/std/special/c.zig
@@ -634,6 +634,16 @@ export fn cosf(a: f32) f32 {
return math.cos(a);
}
+export fn sincos(a: f64, r_sin: *f64, r_cos: *f64) void {
+ r_sin.* = math.sin(a);
+ r_cos.* = math.cos(a);
+}
+
+export fn sincosf(a: f32, r_sin: *f32, r_cos: *f32) void {
+ r_sin.* = math.sin(a);
+ r_cos.* = math.cos(a);
+}
+
export fn exp(a: f64) f64 {
return math.exp(a);
}
diff --git a/lib/std/special/test_runner.zig b/lib/std/special/test_runner.zig
index 2b2fe78262..4bb9202858 100644
--- a/lib/std/special/test_runner.zig
+++ b/lib/std/special/test_runner.zig
@@ -36,7 +36,7 @@ pub fn main() anyerror!void {
}
std.testing.log_level = .warn;
- var test_node = root_node.start(test_fn.name, null);
+ var test_node = root_node.start(test_fn.name, 0);
test_node.activate();
progress.refresh();
if (progress.terminal == null) {
diff --git a/lib/std/start.zig b/lib/std/start.zig
index 7d3a9c45c7..01fe43ca35 100644
--- a/lib/std/start.zig
+++ b/lib/std/start.zig
@@ -10,6 +10,7 @@ const std = @import("std.zig");
const builtin = std.builtin;
const assert = std.debug.assert;
const uefi = std.os.uefi;
+const tlcsprng = @import("crypto/tlcsprng.zig");
var argc_argv_ptr: [*]usize = undefined;
diff --git a/lib/std/std.zig b/lib/std/std.zig
index f6da7afc55..5fbf2662b9 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -29,7 +29,7 @@ pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayE
pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
-pub const Progress = @import("progress.zig").Progress;
+pub const Progress = @import("Progress.zig");
pub const ResetEvent = @import("reset_event.zig").ResetEvent;
pub const SemanticVersion = @import("SemanticVersion.zig");
pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
diff --git a/lib/std/testing.zig b/lib/std/testing.zig
index 69df01190d..63054892db 100644
--- a/lib/std/testing.zig
+++ b/lib/std/testing.zig
@@ -303,8 +303,7 @@ fn getCwdOrWasiPreopen() std.fs.Dir {
pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
- std.crypto.randomBytes(&random_bytes) catch
- @panic("unable to make tmp dir for testing: unable to get random bytes");
+ std.crypto.random.bytes(&random_bytes);
var sub_path: [TmpDir.sub_path_len]u8 = undefined;
std.fs.base64_encoder.encode(&sub_path, &random_bytes);