Merge remote-tracking branch 'origin/master' into copy-elision-3
This commit is contained in:
commit
33371ab55c
|
@ -53,7 +53,7 @@ brew install cmake llvm@8
|
|||
brew outdated llvm@8 || brew upgrade llvm@8
|
||||
mkdir build
|
||||
cd build
|
||||
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/Cellar/llvm/8.0.0
|
||||
cmake .. -DCMAKE_PREFIX_PATH=/usr/local/Cellar/llvm/8.0.0_1
|
||||
make install
|
||||
```
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ pub const Compilation = struct {
|
|||
/// it uses an optional pointer so that tombstone removals are possible
|
||||
fn_link_set: event.Locked(FnLinkSet),
|
||||
|
||||
pub const FnLinkSet = std.LinkedList(?*Value.Fn);
|
||||
pub const FnLinkSet = std.TailQueue(?*Value.Fn);
|
||||
|
||||
windows_subsystem_windows: bool,
|
||||
windows_subsystem_console: bool,
|
||||
|
|
|
@ -186,7 +186,7 @@ pub const Value = struct {
|
|||
/// Path to the object file that contains this function
|
||||
containing_object: Buffer,
|
||||
|
||||
link_set_node: *std.LinkedList(?*Value.Fn).Node,
|
||||
link_set_node: *std.TailQueue(?*Value.Fn).Node,
|
||||
|
||||
/// Creates a Fn value with 1 ref
|
||||
/// Takes ownership of symbol_name
|
||||
|
|
|
@ -890,6 +890,11 @@ static AstNode *ast_parse_if_statement(ParseContext *pc) {
|
|||
body = ast_parse_assign_expr(pc);
|
||||
}
|
||||
|
||||
if (body == nullptr) {
|
||||
Token *tok = eat_token(pc);
|
||||
ast_error(pc, tok, "expected if body, found '%s'", token_name(tok->id));
|
||||
}
|
||||
|
||||
Token *err_payload = nullptr;
|
||||
AstNode *else_body = nullptr;
|
||||
if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
|
||||
|
@ -994,6 +999,11 @@ static AstNode *ast_parse_for_statement(ParseContext *pc) {
|
|||
body = ast_parse_assign_expr(pc);
|
||||
}
|
||||
|
||||
if (body == nullptr) {
|
||||
Token *tok = eat_token(pc);
|
||||
ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id));
|
||||
}
|
||||
|
||||
AstNode *else_body = nullptr;
|
||||
if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
|
||||
else_body = ast_expect(pc, ast_parse_statement);
|
||||
|
@ -1023,6 +1033,11 @@ static AstNode *ast_parse_while_statement(ParseContext *pc) {
|
|||
body = ast_parse_assign_expr(pc);
|
||||
}
|
||||
|
||||
if (body == nullptr) {
|
||||
Token *tok = eat_token(pc);
|
||||
ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id));
|
||||
}
|
||||
|
||||
Token *err_payload = nullptr;
|
||||
AstNode *else_body = nullptr;
|
||||
if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) {
|
||||
|
|
|
@ -14,7 +14,7 @@ pub fn Queue(comptime T: type) type {
|
|||
mutex: std.Mutex,
|
||||
|
||||
pub const Self = @This();
|
||||
pub const Node = std.LinkedList(T).Node;
|
||||
pub const Node = std.TailQueue(T).Node;
|
||||
|
||||
pub fn init() Self {
|
||||
return Self{
|
||||
|
|
|
@ -13,7 +13,7 @@ const BufMap = std.BufMap;
|
|||
const Buffer = std.Buffer;
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const LinkedList = std.LinkedList;
|
||||
const TailQueue = std.TailQueue;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
pub const ChildProcess = struct {
|
||||
|
@ -48,7 +48,7 @@ pub const ChildProcess = struct {
|
|||
pub cwd: ?[]const u8,
|
||||
|
||||
err_pipe: if (os.windows.is_the_target) void else [2]os.fd_t,
|
||||
llnode: if (os.windows.is_the_target) void else LinkedList(*ChildProcess).Node,
|
||||
llnode: if (os.windows.is_the_target) void else TailQueue(*ChildProcess).Node,
|
||||
|
||||
pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError ||
|
||||
os.ChangeCurDirError || windows.CreateProcessError;
|
||||
|
@ -388,7 +388,7 @@ pub const ChildProcess = struct {
|
|||
|
||||
self.pid = pid;
|
||||
self.err_pipe = err_pipe;
|
||||
self.llnode = LinkedList(*ChildProcess).Node.init(self);
|
||||
self.llnode = TailQueue(*ChildProcess).Node.init(self);
|
||||
self.term = null;
|
||||
|
||||
if (self.stdin_behavior == StdIo.Pipe) {
|
||||
|
|
|
@ -2224,8 +2224,9 @@ fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
|
|||
|
||||
fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
const result = mem.readIntSlice(T, ptr.*[0..@sizeOf(T)], endian);
|
||||
ptr.* += @sizeOf(T);
|
||||
const size = (T.bit_count + 7) / 8;
|
||||
const result = mem.readIntSlice(T, ptr.*[0..size], endian);
|
||||
ptr.* += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -887,7 +887,7 @@ pub fn Watch(comptime V: type) type {
|
|||
}
|
||||
|
||||
async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
|
||||
const resolved_path = try std.fs.path.resolve(self.channel.loop.allocator, [][]const u8{file_path});
|
||||
const resolved_path = try std.fs.path.resolve(self.channel.loop.allocator, [_][]const u8{file_path});
|
||||
var resolved_path_consumed = false;
|
||||
defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ pub const Server = struct {
|
|||
waiting_for_emfile_node: PromiseNode,
|
||||
listen_resume_node: event.Loop.ResumeNode,
|
||||
|
||||
const PromiseNode = std.LinkedList(promise).Node;
|
||||
const PromiseNode = std.TailQueue(promise).Node;
|
||||
|
||||
pub fn init(loop: *Loop) Server {
|
||||
// TODO can't initialize handler coroutine here because we need well defined copy elision
|
||||
|
|
|
@ -157,8 +157,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
|
|||
fn ensureCapacityExact(self: *Self, new_capacity: usize) !void {
|
||||
// capacity must always be a power of two to allow for modulo
|
||||
// optimization in the constrainIndex fn
|
||||
const is_power_of_two = new_capacity & (new_capacity - 1) == 0;
|
||||
assert(is_power_of_two);
|
||||
assert(math.isPowerOfTwo(new_capacity));
|
||||
|
||||
if (new_capacity <= self.entries.len) {
|
||||
return;
|
||||
|
|
11
std/heap.zig
11
std/heap.zig
|
@ -347,10 +347,10 @@ pub const ArenaAllocator = struct {
|
|||
pub allocator: Allocator,
|
||||
|
||||
child_allocator: *Allocator,
|
||||
buffer_list: std.LinkedList([]u8),
|
||||
buffer_list: std.SinglyLinkedList([]u8),
|
||||
end_index: usize,
|
||||
|
||||
const BufNode = std.LinkedList([]u8).Node;
|
||||
const BufNode = std.SinglyLinkedList([]u8).Node;
|
||||
|
||||
pub fn init(child_allocator: *Allocator) ArenaAllocator {
|
||||
return ArenaAllocator{
|
||||
|
@ -359,7 +359,7 @@ pub const ArenaAllocator = struct {
|
|||
.shrinkFn = shrink,
|
||||
},
|
||||
.child_allocator = child_allocator,
|
||||
.buffer_list = std.LinkedList([]u8).init(),
|
||||
.buffer_list = std.SinglyLinkedList([]u8).init(),
|
||||
.end_index = 0,
|
||||
};
|
||||
}
|
||||
|
@ -387,10 +387,9 @@ pub const ArenaAllocator = struct {
|
|||
const buf_node = &buf_node_slice[0];
|
||||
buf_node.* = BufNode{
|
||||
.data = buf,
|
||||
.prev = null,
|
||||
.next = null,
|
||||
};
|
||||
self.buffer_list.append(buf_node);
|
||||
self.buffer_list.prepend(buf_node);
|
||||
self.end_index = 0;
|
||||
return buf_node;
|
||||
}
|
||||
|
@ -398,7 +397,7 @@ pub const ArenaAllocator = struct {
|
|||
fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
|
||||
const self = @fieldParentPtr(ArenaAllocator, "allocator", allocator);
|
||||
|
||||
var cur_node = if (self.buffer_list.last) |last_node| last_node else try self.createNode(0, n + alignment);
|
||||
var cur_node = if (self.buffer_list.first) |first_node| first_node else try self.createNode(0, n + alignment);
|
||||
while (true) {
|
||||
const cur_buf = cur_node.data[@sizeOf(BufNode)..];
|
||||
const addr = @ptrToInt(cur_buf.ptr) + self.end_index;
|
||||
|
|
20
std/io.zig
20
std/io.zig
|
@ -164,32 +164,32 @@ pub fn InStream(comptime ReadError: type) type {
|
|||
|
||||
/// Reads a native-endian integer
|
||||
pub fn readIntNative(self: *Self, comptime T: type) !T {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntNative(T, &bytes);
|
||||
}
|
||||
|
||||
/// Reads a foreign-endian integer
|
||||
pub fn readIntForeign(self: *Self, comptime T: type) !T {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntForeign(T, &bytes);
|
||||
}
|
||||
|
||||
pub fn readIntLittle(self: *Self, comptime T: type) !T {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntLittle(T, &bytes);
|
||||
}
|
||||
|
||||
pub fn readIntBig(self: *Self, comptime T: type) !T {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readIntBig(T, &bytes);
|
||||
}
|
||||
|
||||
pub fn readInt(self: *Self, comptime T: type, endian: builtin.Endian) !T {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
try self.readNoEof(bytes[0..]);
|
||||
return mem.readInt(T, &bytes, endian);
|
||||
}
|
||||
|
@ -249,32 +249,32 @@ pub fn OutStream(comptime WriteError: type) type {
|
|||
|
||||
/// Write a native-endian integer.
|
||||
pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
mem.writeIntNative(T, &bytes, value);
|
||||
return self.writeFn(self, bytes);
|
||||
}
|
||||
|
||||
/// Write a foreign-endian integer.
|
||||
pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
mem.writeIntForeign(T, &bytes, value);
|
||||
return self.writeFn(self, bytes);
|
||||
}
|
||||
|
||||
pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
mem.writeIntLittle(T, &bytes, value);
|
||||
return self.writeFn(self, bytes);
|
||||
}
|
||||
|
||||
pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
mem.writeIntBig(T, &bytes, value);
|
||||
return self.writeFn(self, bytes);
|
||||
}
|
||||
|
||||
pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
var bytes: [(T.bit_count + 7 )/ 8]u8 = undefined;
|
||||
mem.writeInt(T, &bytes, value, endian);
|
||||
return self.writeFn(self, bytes);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,192 @@ const testing = std.testing;
|
|||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
/// Generic doubly linked list.
|
||||
pub fn LinkedList(comptime T: type) type {
|
||||
/// A singly-linked list is headed by a single forward pointer. The elements
|
||||
/// are singly linked for minimum space and pointer manipulation overhead at
|
||||
/// the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
/// added to the list after an existing element or at the head of the list.
|
||||
/// A singly-linked list may only be traversed in the forward direction.
|
||||
/// Singly-linked lists are ideal for applications with large datasets and
|
||||
/// few or no removals or for implementing a LIFO queue.
|
||||
pub fn SinglyLinkedList(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
/// Node inside the linked list wrapping the actual data.
|
||||
pub const Node = struct {
|
||||
next: ?*Node,
|
||||
data: T,
|
||||
|
||||
pub fn init(data: T) Node {
|
||||
return Node{
|
||||
.next = null,
|
||||
.data = data,
|
||||
};
|
||||
}
|
||||
|
||||
/// Insert a new node after the current one.
|
||||
///
|
||||
/// Arguments:
|
||||
/// new_node: Pointer to the new node to insert.
|
||||
pub fn insertAfter(node: *Node, new_node: *Node) void {
|
||||
new_node.next = node.next;
|
||||
node.next = new_node;
|
||||
}
|
||||
|
||||
/// Remove a node from the list.
|
||||
///
|
||||
/// Arguments:
|
||||
/// node: Pointer to the node to be removed.
|
||||
/// Returns:
|
||||
/// node removed
|
||||
pub fn removeNext(node: *Node) ?*Node {
|
||||
const next_node = node.next orelse return null;
|
||||
node.next = next_node.next;
|
||||
return next_node;
|
||||
}
|
||||
};
|
||||
|
||||
first: ?*Node,
|
||||
|
||||
/// Initialize a linked list.
|
||||
///
|
||||
/// Returns:
|
||||
/// An empty linked list.
|
||||
pub fn init() Self {
|
||||
return Self{
|
||||
.first = null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Insert a new node after an existing one.
|
||||
///
|
||||
/// Arguments:
|
||||
/// node: Pointer to a node in the list.
|
||||
/// new_node: Pointer to the new node to insert.
|
||||
pub fn insertAfter(list: *Self, node: *Node, new_node: *Node) void {
|
||||
node.insertAfter(new_node);
|
||||
}
|
||||
|
||||
/// Insert a new node at the head.
|
||||
///
|
||||
/// Arguments:
|
||||
/// new_node: Pointer to the new node to insert.
|
||||
pub fn prepend(list: *Self, new_node: *Node) void {
|
||||
new_node.next = list.first;
|
||||
list.first = new_node;
|
||||
}
|
||||
|
||||
/// Remove a node from the list.
|
||||
///
|
||||
/// Arguments:
|
||||
/// node: Pointer to the node to be removed.
|
||||
pub fn remove(list: *Self, node: *Node) void {
|
||||
if (list.first == node) {
|
||||
list.first = node.next;
|
||||
} else {
|
||||
var current_elm = list.first.?;
|
||||
while (current_elm.next != node) {
|
||||
current_elm = current_elm.next.?;
|
||||
}
|
||||
current_elm.next = node.next;
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove and return the first node in the list.
|
||||
///
|
||||
/// Returns:
|
||||
/// A pointer to the first node in the list.
|
||||
pub fn popFirst(list: *Self) ?*Node {
|
||||
const first = list.first orelse return null;
|
||||
list.first = first.next;
|
||||
return first;
|
||||
}
|
||||
|
||||
/// Allocate a new node.
|
||||
///
|
||||
/// Arguments:
|
||||
/// allocator: Dynamic memory allocator.
|
||||
///
|
||||
/// Returns:
|
||||
/// A pointer to the new node.
|
||||
pub fn allocateNode(list: *Self, allocator: *Allocator) !*Node {
|
||||
return allocator.create(Node);
|
||||
}
|
||||
|
||||
/// Deallocate a node.
|
||||
///
|
||||
/// Arguments:
|
||||
/// node: Pointer to the node to deallocate.
|
||||
/// allocator: Dynamic memory allocator.
|
||||
pub fn destroyNode(list: *Self, node: *Node, allocator: *Allocator) void {
|
||||
allocator.destroy(node);
|
||||
}
|
||||
|
||||
/// Allocate and initialize a node and its data.
|
||||
///
|
||||
/// Arguments:
|
||||
/// data: The data to put inside the node.
|
||||
/// allocator: Dynamic memory allocator.
|
||||
///
|
||||
/// Returns:
|
||||
/// A pointer to the new node.
|
||||
pub fn createNode(list: *Self, data: T, allocator: *Allocator) !*Node {
|
||||
var node = try list.allocateNode(allocator);
|
||||
node.* = Node.init(data);
|
||||
return node;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
test "basic SinglyLinkedList test" {
|
||||
const allocator = debug.global_allocator;
|
||||
var list = SinglyLinkedList(u32).init();
|
||||
|
||||
var one = try list.createNode(1, allocator);
|
||||
var two = try list.createNode(2, allocator);
|
||||
var three = try list.createNode(3, allocator);
|
||||
var four = try list.createNode(4, allocator);
|
||||
var five = try list.createNode(5, allocator);
|
||||
defer {
|
||||
list.destroyNode(one, allocator);
|
||||
list.destroyNode(two, allocator);
|
||||
list.destroyNode(three, allocator);
|
||||
list.destroyNode(four, allocator);
|
||||
list.destroyNode(five, allocator);
|
||||
}
|
||||
|
||||
list.prepend(two); // {2}
|
||||
list.insertAfter(two, five); // {2, 5}
|
||||
list.prepend(one); // {1, 2, 5}
|
||||
list.insertAfter(two, three); // {1, 2, 3, 5}
|
||||
list.insertAfter(three, four); // {1, 2, 3, 4, 5}
|
||||
|
||||
// Traverse forwards.
|
||||
{
|
||||
var it = list.first;
|
||||
var index: u32 = 1;
|
||||
while (it) |node| : (it = node.next) {
|
||||
testing.expect(node.data == index);
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
_ = list.popFirst(); // {2, 3, 4, 5}
|
||||
_ = list.remove(five); // {2, 3, 4}
|
||||
_ = two.removeNext(); // {2, 4}
|
||||
|
||||
testing.expect(list.first.?.data == 2);
|
||||
testing.expect(list.first.?.next.?.data == 4);
|
||||
testing.expect(list.first.?.next.?.next == null);
|
||||
}
|
||||
|
||||
/// A tail queue is headed by a pair of pointers, one to the head of the
|
||||
/// list and the other to the tail of the list. The elements are doubly
|
||||
/// linked so that an arbitrary element can be removed without a need to
|
||||
/// traverse the list. New elements can be added to the list before or
|
||||
/// after an existing element, at the head of the list, or at the end of
|
||||
/// the list. A tail queue may be traversed in either direction.
|
||||
pub fn TailQueue(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
||||
|
@ -219,9 +403,9 @@ pub fn LinkedList(comptime T: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
test "basic linked list test" {
|
||||
test "basic TailQueue test" {
|
||||
const allocator = debug.global_allocator;
|
||||
var list = LinkedList(u32).init();
|
||||
var list = TailQueue(u32).init();
|
||||
|
||||
var one = try list.createNode(1, allocator);
|
||||
var two = try list.createNode(2, allocator);
|
||||
|
@ -271,10 +455,10 @@ test "basic linked list test" {
|
|||
testing.expect(list.len == 2);
|
||||
}
|
||||
|
||||
test "linked list concatenation" {
|
||||
test "TailQueue concatenation" {
|
||||
const allocator = debug.global_allocator;
|
||||
var list1 = LinkedList(u32).init();
|
||||
var list2 = LinkedList(u32).init();
|
||||
var list1 = TailQueue(u32).init();
|
||||
var list2 = TailQueue(u32).init();
|
||||
|
||||
var one = try list1.createNode(1, allocator);
|
||||
defer list1.destroyNode(one, allocator);
|
||||
|
|
21
std/math.zig
21
std/math.zig
|
@ -288,10 +288,8 @@ pub fn shl(comptime T: type, a: T, shift_amt: var) T {
|
|||
const abs_shift_amt = absCast(shift_amt);
|
||||
const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
|
||||
|
||||
if (@typeOf(shift_amt).is_signed) {
|
||||
if (shift_amt >= 0) {
|
||||
return a << casted_shift_amt;
|
||||
} else {
|
||||
if (@typeOf(shift_amt) == comptime_int or @typeOf(shift_amt).is_signed) {
|
||||
if (shift_amt < 0) {
|
||||
return a >> casted_shift_amt;
|
||||
}
|
||||
}
|
||||
|
@ -304,6 +302,10 @@ test "math.shl" {
|
|||
testing.expect(shl(u8, 0b11111111, usize(8)) == 0);
|
||||
testing.expect(shl(u8, 0b11111111, usize(9)) == 0);
|
||||
testing.expect(shl(u8, 0b11111111, isize(-2)) == 0b00111111);
|
||||
testing.expect(shl(u8, 0b11111111, 3) == 0b11111000);
|
||||
testing.expect(shl(u8, 0b11111111, 8) == 0);
|
||||
testing.expect(shl(u8, 0b11111111, 9) == 0);
|
||||
testing.expect(shl(u8, 0b11111111, -2) == 0b00111111);
|
||||
}
|
||||
|
||||
/// Shifts right. Overflowed bits are truncated.
|
||||
|
@ -312,7 +314,7 @@ pub fn shr(comptime T: type, a: T, shift_amt: var) T {
|
|||
const abs_shift_amt = absCast(shift_amt);
|
||||
const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt);
|
||||
|
||||
if (@typeOf(shift_amt).is_signed) {
|
||||
if (@typeOf(shift_amt) == comptime_int or @typeOf(shift_amt).is_signed) {
|
||||
if (shift_amt >= 0) {
|
||||
return a >> casted_shift_amt;
|
||||
} else {
|
||||
|
@ -328,6 +330,10 @@ test "math.shr" {
|
|||
testing.expect(shr(u8, 0b11111111, usize(8)) == 0);
|
||||
testing.expect(shr(u8, 0b11111111, usize(9)) == 0);
|
||||
testing.expect(shr(u8, 0b11111111, isize(-2)) == 0b11111100);
|
||||
testing.expect(shr(u8, 0b11111111, 3) == 0b00011111);
|
||||
testing.expect(shr(u8, 0b11111111, 8) == 0);
|
||||
testing.expect(shr(u8, 0b11111111, 9) == 0);
|
||||
testing.expect(shr(u8, 0b11111111, -2) == 0b11111100);
|
||||
}
|
||||
|
||||
/// Rotates right. Only unsigned values can be rotated.
|
||||
|
@ -680,6 +686,11 @@ pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alig
|
|||
return @alignCast(alignment, ptr);
|
||||
}
|
||||
|
||||
pub fn isPowerOfTwo(v: var) bool {
|
||||
assert(v != 0);
|
||||
return (v & (v - 1)) == 0;
|
||||
}
|
||||
|
||||
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
|
||||
var x = value;
|
||||
|
||||
|
|
|
@ -447,7 +447,7 @@ pub const Int = struct {
|
|||
}
|
||||
|
||||
// Power of two: can do a single pass and use masks to extract digits.
|
||||
if (base & (base - 1) == 0) {
|
||||
if (math.isPowerOfTwo(base)) {
|
||||
const base_shift = math.log2_int(Limb, base);
|
||||
|
||||
for (self.limbs[0..self.len()]) |limb| {
|
||||
|
|
|
@ -119,6 +119,23 @@ pub const O_RDONLY = 0o0;
|
|||
pub const O_WRONLY = 0o1;
|
||||
pub const O_RDWR = 0o2;
|
||||
|
||||
pub const kernel_rwf = u32;
|
||||
|
||||
/// high priority request, poll if possible
|
||||
pub const RWF_HIPRI = kernel_rwf(0x00000001);
|
||||
|
||||
/// per-IO O_DSYNC
|
||||
pub const RWF_DSYNC = kernel_rwf(0x00000002);
|
||||
|
||||
/// per-IO O_SYNC
|
||||
pub const RWF_SYNC = kernel_rwf(0x00000004);
|
||||
|
||||
/// per-IO, return -EAGAIN if operation would block
|
||||
pub const RWF_NOWAIT = kernel_rwf(0x00000008);
|
||||
|
||||
/// per-IO O_APPEND
|
||||
pub const RWF_APPEND = kernel_rwf(0x00000010);
|
||||
|
||||
pub const SEEK_SET = 0;
|
||||
pub const SEEK_CUR = 1;
|
||||
pub const SEEK_END = 2;
|
||||
|
@ -950,3 +967,129 @@ pub const stack_t = extern struct {
|
|||
ss_flags: i32,
|
||||
ss_size: isize,
|
||||
};
|
||||
|
||||
pub const io_uring_params = extern struct {
|
||||
sq_entries: u32,
|
||||
cq_entries: u32,
|
||||
flags: u32,
|
||||
sq_thread_cpu: u32,
|
||||
sq_thread_idle: u32,
|
||||
resv: [5]u32,
|
||||
sq_off: io_sqring_offsets,
|
||||
cq_off: io_cqring_offsets,
|
||||
};
|
||||
|
||||
// io_uring_params.flags
|
||||
|
||||
/// io_context is polled
|
||||
pub const IORING_SETUP_IOPOLL = (1 << 0);
|
||||
|
||||
/// SQ poll thread
|
||||
pub const IORING_SETUP_SQPOLL = (1 << 1);
|
||||
|
||||
/// sq_thread_cpu is valid
|
||||
pub const IORING_SETUP_SQ_AFF = (1 << 2);
|
||||
|
||||
pub const io_sqring_offsets = extern struct {
|
||||
/// offset of ring head
|
||||
head: u32,
|
||||
|
||||
/// offset of ring tail
|
||||
tail: u32,
|
||||
|
||||
/// ring mask value
|
||||
ring_mask: u32,
|
||||
|
||||
/// entries in ring
|
||||
ring_entries: u32,
|
||||
|
||||
/// ring flags
|
||||
flags: u32,
|
||||
|
||||
/// number of sqes not submitted
|
||||
dropped: u32,
|
||||
|
||||
/// sqe index array
|
||||
array: u32,
|
||||
|
||||
resv1: u32,
|
||||
resv2: u64,
|
||||
};
|
||||
|
||||
// io_sqring_offsets.flags
|
||||
|
||||
/// needs io_uring_enter wakeup
|
||||
pub const IORING_SQ_NEED_WAKEUP = 1 << 0;
|
||||
|
||||
pub const io_cqring_offsets = extern struct {
|
||||
head: u32,
|
||||
tail: u32,
|
||||
ring_mask: u32,
|
||||
ring_entries: u32,
|
||||
overflow: u32,
|
||||
cqes: u32,
|
||||
resv: [2]u64,
|
||||
};
|
||||
|
||||
pub const io_uring_sqe = extern struct {
|
||||
opcode: u8,
|
||||
flags: u8,
|
||||
ioprio: u16,
|
||||
fd: i32,
|
||||
off: u64,
|
||||
addr: u64,
|
||||
len: u32,
|
||||
pub const union1 = extern union {
|
||||
rw_flags: kernel_rwf,
|
||||
fsync_flags: u32,
|
||||
poll_event: u16,
|
||||
};
|
||||
union1: union1,
|
||||
user_data: u64,
|
||||
pub const union2 = extern union {
|
||||
buf_index: u16,
|
||||
__pad2: [3]u64,
|
||||
};
|
||||
union2: union2,
|
||||
};
|
||||
|
||||
// io_uring_sqe.flags
|
||||
|
||||
/// use fixed fileset
|
||||
pub const IOSQE_FIXED_FILE = (1 << 0);
|
||||
|
||||
pub const IORING_OP_NOP = 0;
|
||||
pub const IORING_OP_READV = 1;
|
||||
pub const IORING_OP_WRITEV = 2;
|
||||
pub const IORING_OP_FSYNC = 3;
|
||||
pub const IORING_OP_READ_FIXED = 4;
|
||||
pub const IORING_OP_WRITE_FIXED = 5;
|
||||
pub const IORING_OP_POLL_ADD = 6;
|
||||
pub const IORING_OP_POLL_REMOVE = 7;
|
||||
|
||||
// io_uring_sqe.fsync_flags
|
||||
pub const IORING_FSYNC_DATASYNC = (1 << 0);
|
||||
|
||||
// IO completion data structure (Completion Queue Entry)
|
||||
pub const io_uring_cqe = extern struct {
|
||||
/// io_uring_sqe.data submission passed back
|
||||
user_data: u64,
|
||||
|
||||
/// result code for this event
|
||||
res: i32,
|
||||
flags: u32,
|
||||
};
|
||||
|
||||
pub const IORING_OFF_SQ_RING = 0;
|
||||
pub const IORING_OFF_CQ_RING = 0x8000000;
|
||||
pub const IORING_OFF_SQES = 0x10000000;
|
||||
|
||||
// io_uring_enter flags
|
||||
pub const IORING_ENTER_GETEVENTS = (1 << 0);
|
||||
pub const IORING_ENTER_SQ_WAKEUP = (1 << 1);
|
||||
|
||||
// io_uring_register opcodes and arguments
|
||||
pub const IORING_REGISTER_BUFFERS = 0;
|
||||
pub const IORING_UNREGISTER_BUFFERS = 1;
|
||||
pub const IORING_REGISTER_FILES = 2;
|
||||
pub const IORING_UNREGISTER_FILES = 3;
|
||||
|
|
|
@ -189,6 +189,10 @@ pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize {
|
|||
return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset);
|
||||
}
|
||||
|
||||
pub fn preadv2(fd: i32, iov: [*]const iovec, count: usize, offset: u64, flags: kernel_rwf) usize {
|
||||
return syscall5(SYS_preadv2, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset, flags);
|
||||
}
|
||||
|
||||
pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize {
|
||||
return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count);
|
||||
}
|
||||
|
@ -201,6 +205,10 @@ pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) us
|
|||
return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset);
|
||||
}
|
||||
|
||||
pub fn pwritev2(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64, flags: kernel_rwf) usize {
|
||||
return syscall5(SYS_pwritev2, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset, flags);
|
||||
}
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/265
|
||||
pub fn rmdir(path: [*]const u8) usize {
|
||||
if (@hasDecl(@This(), "SYS_rmdir")) {
|
||||
|
@ -887,6 +895,18 @@ pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_inf
|
|||
return last_r;
|
||||
}
|
||||
|
||||
pub fn io_uring_setup(entries: u32, p: *io_uring_params) usize {
|
||||
return syscall2(SYS_io_uring_setup, entries, @ptrToInt(p));
|
||||
}
|
||||
|
||||
pub fn io_uring_enter(fd: i32, to_submit: u32, min_complete: u32, flags: u32, sig: ?*sigset_t) usize {
|
||||
return syscall6(SYS_io_uring_enter, @bitCast(usize, isize(fd)), to_submit, min_complete, flags, @ptrToInt(sig), NSIG / 8);
|
||||
}
|
||||
|
||||
pub fn io_uring_register(fd: i32, opcode: u32, arg: ?*const c_void, nr_args: u32) usize {
|
||||
return syscall4(SYS_io_uring_register, @bitCast(usize, isize(fd)), opcode, @ptrToInt(arg), nr_args);
|
||||
}
|
||||
|
||||
test "" {
|
||||
if (is_the_target) {
|
||||
_ = @import("linux/test.zig");
|
||||
|
|
100
std/rand.zig
100
std/rand.zig
|
@ -18,6 +18,7 @@ const std = @import("std.zig");
|
|||
const builtin = @import("builtin");
|
||||
const assert = std.debug.assert;
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
const ziggurat = @import("rand/ziggurat.zig");
|
||||
|
@ -935,6 +936,105 @@ test "isaac64 sequence" {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
|
|
@ -80,9 +80,9 @@ pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type
|
|||
const prealloc_exp = blk: {
|
||||
// we don't use the prealloc_exp constant when prealloc_item_count is 0.
|
||||
assert(prealloc_item_count != 0);
|
||||
assert(std.math.isPowerOfTwo(prealloc_item_count));
|
||||
|
||||
const value = std.math.log2_int(usize, prealloc_item_count);
|
||||
assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2
|
||||
break :blk @typeOf(1)(value);
|
||||
};
|
||||
const ShelfIndex = std.math.Log2Int(usize);
|
||||
|
|
|
@ -7,17 +7,18 @@ pub const Buffer = @import("buffer.zig").Buffer;
|
|||
pub const BufferOutStream = @import("io.zig").BufferOutStream;
|
||||
pub const DynLib = @import("dynamic_library.zig").DynLib;
|
||||
pub const HashMap = @import("hash_map.zig").HashMap;
|
||||
pub const LinkedList = @import("linked_list.zig").LinkedList;
|
||||
pub const Mutex = @import("mutex.zig").Mutex;
|
||||
pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
|
||||
pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
|
||||
pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
|
||||
pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
|
||||
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
|
||||
pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
|
||||
pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
|
||||
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
||||
pub const SpinLock = @import("spinlock.zig").SpinLock;
|
||||
pub const ChildProcess = @import("child_process.zig").ChildProcess;
|
||||
pub const TailQueue = @import("linked_list.zig").TailQueue;
|
||||
pub const Thread = @import("thread.zig").Thread;
|
||||
|
||||
pub const atomic = @import("atomic.zig");
|
||||
|
|
|
@ -78,8 +78,10 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
|
|||
|
||||
TypeId.Array => |array| expectEqualSlices(array.child, &expected, &actual),
|
||||
|
||||
TypeId.Struct => {
|
||||
@compileError("TODO implement testing.expectEqual for structs");
|
||||
TypeId.Struct => |structType| {
|
||||
inline for (structType.fields) |field| {
|
||||
expectEqual(@field(expected, field.name), @field(actual, field.name));
|
||||
}
|
||||
},
|
||||
|
||||
TypeId.Union => |union_info| {
|
||||
|
|
|
@ -230,6 +230,33 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
|||
"tmp.zig:10:25: error: expression value is ignored",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"empty while loop body",
|
||||
\\export fn a() void {
|
||||
\\ while(true);
|
||||
\\}
|
||||
,
|
||||
"tmp.zig:2:16: error: expected loop body, found ';'",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"empty for loop body",
|
||||
\\export fn a() void {
|
||||
\\ for(undefined) |x|;
|
||||
\\}
|
||||
,
|
||||
"tmp.zig:2:23: error: expected loop body, found ';'",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"empty if body",
|
||||
\\export fn a() void {
|
||||
\\ if(true);
|
||||
\\}
|
||||
,
|
||||
"tmp.zig:2:13: error: expected if body, found ';'",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"import outside package path",
|
||||
\\comptime{
|
||||
|
|
Loading…
Reference in New Issue
Block a user