aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xci/srht/update_download_page8
-rw-r--r--lib/std/array_list.zig2
-rw-r--r--lib/std/builtin.zig2
-rw-r--r--lib/std/child_process.zig2
-rw-r--r--lib/std/debug/failing_allocator.zig4
-rw-r--r--lib/std/event/fs.zig1392
-rw-r--r--lib/std/fifo.zig2
-rw-r--r--lib/std/mutex.zig4
-rw-r--r--lib/std/net.zig2
-rw-r--r--lib/std/os.zig4
-rw-r--r--lib/std/os/bits/linux.zig1
-rw-r--r--lib/std/os/uefi/protocols/hii.zig1
-rw-r--r--lib/std/os/uefi/status.zig38
-rw-r--r--lib/std/os/uefi/tables/table_header.zig1
-rw-r--r--lib/std/os/windows.zig5
-rw-r--r--lib/std/os/windows/ws2_32.zig10
-rw-r--r--lib/std/zig/ast.zig2
-rw-r--r--lib/std/zig/parse.zig42
-rw-r--r--lib/std/zig/parser_test.zig27
-rw-r--r--lib/std/zig/render.zig9
20 files changed, 806 insertions, 752 deletions
diff --git a/ci/srht/update_download_page b/ci/srht/update_download_page
index dc6b1bcc9a..1a721bec80 100755
--- a/ci/srht/update_download_page
+++ b/ci/srht/update_download_page
@@ -73,7 +73,7 @@ cd www.ziglang.org
export MASTER_DATE="$(date +%Y-%m-%d)"
"../$ZIG" run update-download-page.zig
-$S3CMD put -P --add-header="cache-control: public, max-age=31536000, immutable" "../$SRC_TARBALL" s3://ziglang.org/builds/
-$S3CMD put -P "../$LANGREF" s3://ziglang.org/documentation/master/index.html --add-header="Cache-Control: max-age=0, must-revalidate"
-$S3CMD put -P www/download/index.html s3://ziglang.org/download/index.html --add-header="Cache-Control: max-age=0, must-revalidate"
-$S3CMD put -P www/download/index.json s3://ziglang.org/download/index.json --add-header="Cache-Control: max-age=0, must-revalidate"
+$S3CMD put -P --no-mime-magic --add-header="cache-control: public, max-age=31536000, immutable" "../$SRC_TARBALL" s3://ziglang.org/builds/
+$S3CMD put -P --no-mime-magic "../$LANGREF" s3://ziglang.org/documentation/master/index.html --add-header="Cache-Control: max-age=0, must-revalidate"
+$S3CMD put -P --no-mime-magic www/download/index.html s3://ziglang.org/download/index.html --add-header="Cache-Control: max-age=0, must-revalidate"
+$S3CMD put -P --no-mime-magic www/download/index.json s3://ziglang.org/download/index.json --add-header="Cache-Control: max-age=0, must-revalidate"
diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig
index 59fd2a10e5..26342d7833 100644
--- a/lib/std/array_list.zig
+++ b/lib/std/array_list.zig
@@ -58,7 +58,7 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
return self.items[0..self.len];
}
- /// Safely access index i of the list.
+ /// Safely access index i of the list.
pub fn at(self: Self, i: usize) T {
return self.toSliceConst()[i];
}
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index af045c5231..689c5cd898 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -144,6 +144,7 @@ pub const TypeInfo = union(enum) {
alignment: comptime_int,
child: type,
is_allowzero: bool,
+
/// The type of the sentinel is the element type of the pointer, which is
/// the value of the `child` field in this struct. However there is no way
/// to refer to that type here, so we use `var`.
@@ -164,6 +165,7 @@ pub const TypeInfo = union(enum) {
pub const Array = struct {
len: comptime_int,
child: type,
+
/// The type of the sentinel is the element type of the array, which is
/// the value of the `child` field in this struct. However there is no way
/// to refer to that type here, so we use `var`.
diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig
index 866b3ad67e..36621758b2 100644
--- a/lib/std/child_process.zig
+++ b/lib/std/child_process.zig
@@ -259,7 +259,7 @@ pub const ChildProcess = struct {
}
fn handleWaitResult(self: *ChildProcess, status: u32) void {
- // TODO https://github.com/ziglang/zig/issues/3190
+ // TODO https://github.com/ziglang/zig/issues/3190
var term = self.cleanupAfterWait(status);
self.term = term;
}
diff --git a/lib/std/debug/failing_allocator.zig b/lib/std/debug/failing_allocator.zig
index 6afd7c4880..081a29cd97 100644
--- a/lib/std/debug/failing_allocator.zig
+++ b/lib/std/debug/failing_allocator.zig
@@ -5,10 +5,10 @@ const mem = std.mem;
/// memory conditions are handled correctly.
///
/// To use this, first initialize it and get an allocator with
-///
+///
/// `const failing_allocator = &FailingAllocator.init(<allocator>,
/// <fail_index>).allocator;`
-///
+///
/// Then use `failing_allocator` anywhere you would have used a
/// different allocator.
pub const FailingAllocator = struct {
diff --git a/lib/std/event/fs.zig b/lib/std/event/fs.zig
index 965302e1e6..a4b6488344 100644
--- a/lib/std/event/fs.zig
+++ b/lib/std/event/fs.zig
@@ -9,6 +9,12 @@ const windows = os.windows;
const Loop = event.Loop;
const fd_t = os.fd_t;
const File = std.fs.File;
+const Allocator = mem.Allocator;
+
+//! TODO mege this with `std.fs`
+
+const global_event_loop = Loop.instance orelse
+ @compileError("std.event.fs currently only works with event-based I/O");
pub const RequestNode = std.atomic.Queue(Request).Node;
@@ -84,7 +90,7 @@ pub const Request = struct {
pub const PWriteVError = error{OutOfMemory} || File.WriteError;
/// data - just the inner references - must live until pwritev frame completes.
-pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {
+pub fn pwritev(allocator: *Allocator, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {
switch (builtin.os) {
.macosx,
.linux,
@@ -92,8 +98,8 @@ pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) P
.netbsd,
.dragonfly,
=> {
- const iovecs = try loop.allocator.alloc(os.iovec_const, data.len);
- defer loop.allocator.free(iovecs);
+ const iovecs = try allocator.alloc(os.iovec_const, data.len);
+ defer allocator.free(iovecs);
for (data) |buf, i| {
iovecs[i] = os.iovec_const{
@@ -102,31 +108,31 @@ pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) P
};
}
- return pwritevPosix(loop, fd, iovecs, offset);
+ return pwritevPosix(fd, iovecs, offset);
},
.windows => {
- const data_copy = try std.mem.dupe(loop.allocator, []const u8, data);
- defer loop.allocator.free(data_copy);
- return pwritevWindows(loop, fd, data, offset);
+ const data_copy = try std.mem.dupe(allocator, []const u8, data);
+ defer allocator.free(data_copy);
+ return pwritevWindows(fd, data, offset);
},
else => @compileError("Unsupported OS"),
}
}
/// data must outlive the returned frame
-pub fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {
+pub fn pwritevWindows(fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {
if (data.len == 0) return;
- if (data.len == 1) return pwriteWindows(loop, fd, data[0], offset);
+ if (data.len == 1) return pwriteWindows(fd, data[0], offset);
// TODO do these in parallel
var off = offset;
for (data) |buf| {
- try pwriteWindows(loop, fd, buf, off);
+ try pwriteWindows(fd, buf, off);
off += buf.len;
}
}
-pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {
+pub fn pwriteWindows(fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {
var resume_node = Loop.ResumeNode.Basic{
.base = Loop.ResumeNode{
.id = Loop.ResumeNode.Id.Basic,
@@ -141,9 +147,9 @@ pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.Wi
},
};
// TODO only call create io completion port once per fd
- _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined);
- loop.beginOneEvent();
- errdefer loop.finishOneEvent();
+ _ = windows.CreateIoCompletionPort(fd, global_event_loop.os_data.io_port, undefined, undefined);
+ global_event_loop.beginOneEvent();
+ errdefer global_event_loop.finishOneEvent();
errdefer {
_ = windows.kernel32.CancelIoEx(fd, &resume_node.base.overlapped);
@@ -166,12 +172,7 @@ pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.Wi
}
/// iovecs must live until pwritev frame completes.
-pub fn pwritevPosix(
- loop: *Loop,
- fd: fd_t,
- iovecs: []const os.iovec_const,
- offset: usize,
-) os.WriteError!void {
+pub fn pwritevPosix(fd: fd_t, iovecs: []const os.iovec_const, offset: usize) os.WriteError!void {
var req_node = RequestNode{
.prev = null,
.next = null,
@@ -194,21 +195,17 @@ pub fn pwritevPosix(
},
};
- errdefer loop.posixFsCancel(&req_node);
+ errdefer global_event_loop.posixFsCancel(&req_node);
suspend {
- loop.posixFsRequest(&req_node);
+ global_event_loop.posixFsRequest(&req_node);
}
return req_node.data.msg.PWriteV.result;
}
/// iovecs must live until pwritev frame completes.
-pub fn writevPosix(
- loop: *Loop,
- fd: fd_t,
- iovecs: []const os.iovec_const,
-) os.WriteError!void {
+pub fn writevPosix(fd: fd_t, iovecs: []const os.iovec_const) os.WriteError!void {
var req_node = RequestNode{
.prev = null,
.next = null,
@@ -231,7 +228,7 @@ pub fn writevPosix(
};
suspend {
- loop.posixFsRequest(&req_node);
+ global_event_loop.posixFsRequest(&req_node);
}
return req_node.data.msg.WriteV.result;
@@ -240,7 +237,7 @@ pub fn writevPosix(
pub const PReadVError = error{OutOfMemory} || File.ReadError;
/// data - just the inner references - must live until preadv frame completes.
-pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {
+pub fn preadv(allocator: *Allocator, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {
assert(data.len != 0);
switch (builtin.os) {
.macosx,
@@ -249,8 +246,8 @@ pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVEr
.netbsd,
.dragonfly,
=> {
- const iovecs = try loop.allocator.alloc(os.iovec, data.len);
- defer loop.allocator.free(iovecs);
+ const iovecs = try allocator.alloc(os.iovec, data.len);
+ defer allocator.free(iovecs);
for (data) |buf, i| {
iovecs[i] = os.iovec{
@@ -259,21 +256,21 @@ pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVEr
};
}
- return preadvPosix(loop, fd, iovecs, offset);
+ return preadvPosix(fd, iovecs, offset);
},
.windows => {
- const data_copy = try std.mem.dupe(loop.allocator, []u8, data);
- defer loop.allocator.free(data_copy);
- return preadvWindows(loop, fd, data_copy, offset);
+ const data_copy = try std.mem.dupe(allocator, []u8, data);
+ defer allocator.free(data_copy);
+ return preadvWindows(fd, data_copy, offset);
},
else => @compileError("Unsupported OS"),
}
}
/// data must outlive the returned frame
-pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {
+pub fn preadvWindows(fd: fd_t, data: []const []u8, offset: u64) !usize {
assert(data.len != 0);
- if (data.len == 1) return preadWindows(loop, fd, data[0], offset);
+ if (data.len == 1) return preadWindows(fd, data[0], offset);
// TODO do these in parallel?
var off: usize = 0;
@@ -281,7 +278,7 @@ pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !us
var inner_off: usize = 0;
while (true) {
const v = data[iov_i];
- const amt_read = try preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off);
+ const amt_read = try preadWindows(fd, v[inner_off .. v.len - inner_off], offset + off);
off += amt_read;
inner_off += amt_read;
if (inner_off == v.len) {
@@ -295,7 +292,7 @@ pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !us
}
}
-pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {
+pub fn preadWindows(fd: fd_t, data: []u8, offset: u64) !usize {
var resume_node = Loop.ResumeNode.Basic{
.base = Loop.ResumeNode{
.id = Loop.ResumeNode.Id.Basic,
@@ -310,9 +307,9 @@ pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {
},
};
// TODO only call create io completion port once per fd
- _ = windows.CreateIoCompletionPort(fd, loop.os_data.io_port, undefined, undefined) catch undefined;
- loop.beginOneEvent();
- errdefer loop.finishOneEvent();
+ _ = windows.CreateIoCompletionPort(fd, global_event_loop.os_data.io_port, undefined, undefined) catch undefined;
+ global_event_loop.beginOneEvent();
+ errdefer global_event_loop.finishOneEvent();
errdefer {
_ = windows.kernel32.CancelIoEx(fd, &resume_node.base.overlapped);
@@ -334,12 +331,7 @@ pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {
}
/// iovecs must live until preadv frame completes
-pub fn preadvPosix(
- loop: *Loop,
- fd: fd_t,
- iovecs: []const os.iovec,
- offset: usize,
-) os.ReadError!usize {
+pub fn preadvPosix(fd: fd_t, iovecs: []const os.iovec, offset: usize) os.ReadError!usize {
var req_node = RequestNode{
.prev = null,
.next = null,
@@ -362,21 +354,16 @@ pub fn preadvPosix(
},
};
- errdefer loop.posixFsCancel(&req_node);
+ errdefer global_event_loop.posixFsCancel(&req_node);
suspend {
- loop.posixFsRequest(&req_node);
+ global_event_loop.posixFsRequest(&req_node);
}
return req_node.data.msg.PReadV.result;
}
-pub fn openPosix(
- loop: *Loop,
- path: []const u8,
- flags: u32,
- mode: File.Mode,
-) File.OpenError!fd_t {
+pub fn openPosix(path: []const u8, flags: u32, mode: File.Mode) File.OpenError!fd_t {
const path_c = try std.os.toPosixPath(path);
var req_node = RequestNode{
@@ -401,21 +388,21 @@ pub fn openPosix(
},
};
- errdefer loop.posixFsCancel(&req_node);
+ errdefer global_event_loop.posixFsCancel(&req_node);
suspend {
- loop.posixFsRequest(&req_node);
+ global_event_loop.posixFsRequest(&req_node);
}
return req_node.data.msg.Open.result;
}
-pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {
+pub fn openRead(path: []const u8) File.OpenError!fd_t {
switch (builtin.os) {
.macosx, .linux, .freebsd, .netbsd, .dragonfly => {
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
const flags = O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
- return openPosix(loop, path, flags, File.default_mode);
+ return openPosix(path, flags, File.default_mode);
},
.windows => return windows.CreateFile(
@@ -434,12 +421,12 @@ pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {
/// Creates if does not exist. Truncates the file if it exists.
/// Uses the default mode.
-pub fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t {
- return openWriteMode(loop, path, File.default_mode);
+pub fn openWrite(path: []const u8) File.OpenError!fd_t {
+ return openWriteMode(path, File.default_mode);
}
/// Creates if does not exist. Truncates the file if it exists.
-pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t {
+pub fn openWriteMode(path: []const u8, mode: File.Mode) File.OpenError!fd_t {
switch (builtin.os) {
.macosx,
.linux,
@@ -449,7 +436,7 @@ pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenEr
=> {
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
- return openPosix(loop, path, flags, File.default_mode);
+ return openPosix(path, flags, File.default_mode);
},
.windows => return windows.CreateFile(
path,
@@ -465,16 +452,12 @@ pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenEr
}
/// Creates if does not exist. Does not truncate.
-pub fn openReadWrite(
- loop: *Loop,
- path: []const u8,
- mode: File.Mode,
-) File.OpenError!fd_t {
+pub fn openReadWrite(path: []const u8, mode: File.Mode) File.OpenError!fd_t {
switch (builtin.os) {
.macosx, .linux, .freebsd, .netbsd, .dragonfly => {
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
const flags = O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC;
- return openPosix(loop, path, flags, mode);
+ return openPosix(path, flags, mode);
},
.windows => return windows.CreateFile(
@@ -498,7 +481,7 @@ pub fn openReadWrite(
/// If you call `setHandle` then finishing will close the fd; otherwise finishing
/// will deallocate the `CloseOperation`.
pub const CloseOperation = struct {
- loop: *Loop,
+ allocator: *Allocator,
os_data: OsData,
const OsData = switch (builtin.os) {
@@ -516,10 +499,10 @@ pub const CloseOperation = struct {
close_req_node: RequestNode,
};
- pub fn start(loop: *Loop) (error{OutOfMemory}!*CloseOperation) {
- const self = try loop.allocator.create(CloseOperation);
+ pub fn start(allocator: *Allocator) (error{OutOfMemory}!*CloseOperation) {
+ const self = try allocator.create(CloseOperation);
self.* = CloseOperation{
- .loop = loop,
+ .allocator = allocator,
.os_data = switch (builtin.os) {
.linux, .macosx, .freebsd, .netbsd, .dragonfly => initOsDataPosix(self),
.windows => OsData{ .handle = null },
@@ -555,16 +538,16 @@ pub const CloseOperation = struct {
.dragonfly,
=> {
if (self.os_data.have_fd) {
- self.loop.posixFsRequest(&self.os_data.close_req_node);
+ global_event_loop.posixFsRequest(&self.os_data.close_req_node);
} else {
- self.loop.allocator.destroy(self);
+ self.allocator.destroy(self);
}
},
.windows => {
if (self.os_data.handle) |handle| {
os.close(handle);
}
- self.loop.allocator.destroy(self);
+ self.allocator.destroy(self);
},
else => @compileError("Unsupported OS"),
}
@@ -627,25 +610,25 @@ pub const CloseOperation = struct {
/// contents must remain alive until writeFile completes.
/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate
-pub fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {
- return writeFileMode(loop, path, contents, File.default_mode);
+pub fn writeFile(allocator: *Allocator, path: []const u8, contents: []const u8) !void {
+ return writeFileMode(allocator, path, contents, File.default_mode);
}
/// contents must remain alive until writeFile completes.
-pub fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
+pub fn writeFileMode(allocator: *Allocator, path: []const u8, contents: []const u8, mode: File.Mode) !void {
switch (builtin.os) {
.linux,
.macosx,
.freebsd,
.netbsd,
.dragonfly,
- => return writeFileModeThread(loop, path, contents, mode),
- .windows => return writeFileWindows(loop, path, contents),
+ => return writeFileModeThread(allocator, path, contents, mode),
+ .windows => return writeFileWindows(path, contents),
else => @compileError("Unsupported OS"),
}
}
-fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
+fn writeFileWindows(path: []const u8, contents: []const u8) !void {
const handle = try windows.CreateFile(
path,
windows.GENERIC_WRITE,
@@ -657,12 +640,12 @@ fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
);
defer os.close(handle);
- try pwriteWindows(loop, handle, contents, 0);
+ try pwriteWindows(handle, contents, 0);
}
-fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
- const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
- defer loop.allocator.free(path_with_null);
+fn writeFileModeThread(allocator: *Allocator, path: []const u8, contents: []const u8, mode: File.Mode) !void {
+ const path_with_null = try std.cstr.addNullByte(allocator, path);
+ defer allocator.free(path_with_null);
var req_node = RequestNode{
.prev = null,
@@ -686,10 +669,10 @@ fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode
},
};
- errdefer loop.posixFsCancel(&req_node);
+ errdefer global_event_loop.posixFsCancel(&req_node);
suspend {
- loop.posixFsRequest(&req_node);
+ global_event_loop.posixFsRequest(&req_node);
}
return req_node.data.msg.WriteFile.result;
@@ -698,21 +681,21 @@ fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode
/// The frame resumes when the last data has been confirmed written, but before the file handle
/// is closed.
/// Caller owns returned memory.
-pub fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
- var close_op = try CloseOperation.start(loop);
+pub fn readFile(allocator: *Allocator, file_path: []const u8, max_size: usize) ![]u8 {
+ var close_op = try CloseOperation.start(allocator);
defer close_op.finish();
- const fd = try openRead(loop, file_path);
+ const fd = try openRead(file_path);
close_op.setHandle(fd);
- var list = std.ArrayList(u8).init(loop.allocator);
+ var list = std.ArrayList(u8).init(allocator);
defer list.deinit();
while (true) {
try list.ensureCapacity(list.len + mem.page_size);
const buf = list.items[list.len..];
const buf_array = [_][]u8{buf};
- const amt = try preadv(loop, fd, buf_array, list.len);
+ const amt = try preadv(allocator, fd, buf_array, list.len);
list.len += amt;
if (list.len > max_size) {
return error.FileTooBig;
@@ -738,610 +721,603 @@ fn hashString(s: []const u16) u32 {
return @truncate(u32, std.hash.Wyhash.hash(0, @sliceToBytes(s)));
}
-//pub const WatchEventError = error{
-// UserResourceLimitReached,
-// SystemResources,
-// AccessDenied,
-// Unexpected, // TODO remove this possibility
-//};
-//
-//pub fn Watch(comptime V: type) type {
-// return struct {
-// channel: *event.Channel(Event.Error!Event),
-// os_data: OsData,
-//
-// const OsData = switch (builtin.os) {
-// .macosx, .freebsd, .netbsd, .dragonfly => struct {
-// file_table: FileTable,
-// table_lock: event.Lock,
-//
-// const FileTable = std.StringHashmap(*Put);
-// const Put = struct {
-// putter: anyframe,
-// value_ptr: *V,
-// };
-// },
-//
-// .linux => LinuxOsData,
-// .windows => WindowsOsData,
-//
-// else => @compileError("Unsupported OS"),
-// };
-//
-// const WindowsOsData = struct {
-// table_lock: event.Lock,
-// dir_table: DirTable,
-// all_putters: std.atomic.Queue(anyframe),
-// ref_count: std.atomic.Int(usize),
-//
-// const DirTable = std.StringHashMap(*Dir);
-// const FileTable = std.HashMap([]const u16, V, hashString, eqlString);
-//
-// const Dir = struct {
-// putter: anyframe,
-// file_table: FileTable,
-// table_lock: event.Lock,
-// };
-// };
-//
-// const LinuxOsData = struct {
-// putter: anyframe,
-// inotify_fd: i32,
-// wd_table: WdTable,
-// table_lock: event.Lock,
-//
-// const WdTable = std.AutoHashMap(i32, Dir);
-// const FileTable = std.StringHashMap(V);
-//
-// const Dir = struct {
-// dirname: []const u8,
-// file_table: FileTable,
-// };
-// };
-//
-// const FileToHandle = std.StringHashMap(anyframe);
-//
-// const Self = @This();
-//
-// pub const Event = struct {
-// id: Id,
-// data: V,
-//
-// pub const Id = WatchEventId;
-// pub const Error = WatchEventError;
-// };
-//
-// pub fn create(loop: *Loop, event_buf_count: usize) !*Self {
-// const channel = try event.Channel(Self.Event.Error!Self.Event).create(loop, event_buf_count);
-// errdefer channel.destroy();
-//
-// switch (builtin.os) {
-// .linux => {
-// const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
-// errdefer os.close(inotify_fd);
-//
-// var result: *Self = undefined;
-// _ = try async<loop.allocator> linuxEventPutter(inotify_fd, channel, &result);
-// return result;
-// },
-//
-// .windows => {
-// const self = try loop.allocator.create(Self);
-// errdefer loop.allocator.destroy(self);
-// self.* = Self{
-// .channel = channel,
-// .os_data = OsData{
-// .table_lock = event.Lock.init(loop),
-// .dir_table = OsData.DirTable.init(loop.allocator),
-// .ref_count = std.atomic.Int(usize).init(1),
-// .all_putters = std.atomic.Queue(anyframe).init(),
-// },
-// };
-// return self;
-// },
-//
-// .macosx, .freebsd, .netbsd, .dragonfly => {
-// const self = try loop.allocator.create(Self);
-// errdefer loop.allocator.destroy(self);
-//
-// self.* = Self{
-// .channel = channel,
-// .os_data = OsData{
-// .table_lock = event.Lock.init(loop),
-// .file_table = OsData.FileTable.init(loop.allocator),
-// },
-// };
-// return self;
-// },
-// else => @compileError("Unsupported OS"),
-// }
-// }
-//
-// /// All addFile calls and removeFile calls must have completed.
-// pub fn destroy(self: *Self) void {
-// switch (builtin.os) {
-// .macosx, .freebsd, .netbsd, .dragonfly => {
-// // TODO we need to cancel the frames before destroying the lock
-// self.os_data.table_lock.deinit();
-// var it = self.os_data.file_table.iterator();
-// while (it.next()) |entry| {
-// cancel entry.value.putter;
-// self.channel.loop.allocator.free(entry.key);
-// }
-// self.channel.destroy();
-// },
-// .linux => cancel self.os_data.putter,
-// .windows => {
-// while (self.os_data.all_putters.get()) |putter_node| {
-// cancel putter_node.data;
-// }
-// self.deref();
-// },
-// else => @compileError("Unsupported OS"),
-// }
-// }
-//
-// fn ref(self: *Self) void {
-// _ = self.os_data.ref_count.incr();
-// }
-//
-// fn deref(self: *Self) void {
-// if (self.os_data.ref_count.decr() == 1) {
-// const allocator = self.channel.loop.allocator;
-// self.os_data.table_lock.deinit();
-// var it = self.os_data.dir_table.iterator();
-// while (it.next()) |entry| {
-// allocator.free(entry.key);
-// allocator.destroy(entry.value);
-// }
-// self.os_data.dir_table.deinit();
-// self.channel.destroy();
-// allocator.destroy(self);
-// }
-// }
-//
-// pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
-// switch (builtin.os) {
-// .macosx, .freebsd, .netbsd, .dragonfly => return await (async addFileKEvent(self, file_path, value) catch unreachable),
-// .linux => return await (async addFileLinux(self, file_path, value) catch unreachable),
-// .windows => return await (async addFileWindows(self, file_path, value) catch unreachable),
-// else => @compileError("Unsupported OS"),
-// }
-// }
-//
-// 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});
-// var resolved_path_consumed = false;
-// defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
-//
-// var close_op = try CloseOperation.start(self.channel.loop);
-// var close_op_consumed = false;
-// defer if (!close_op_consumed) close_op.finish();
-//
-// const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0;
-// const mode = 0;
-// const fd = try await (async openPosix(self.channel.loop, resolved_path, flags, mode) catch unreachable);
-// close_op.setHandle(fd);
-//
-// var put_data: *OsData.Put = undefined;
-// const putter = try async self.kqPutEvents(close_op, value, &put_data);
-// close_op_consumed = true;
-// errdefer cancel putter;
-//
-// const result = blk: {
-// const held = await (async self.os_data.table_lock.acquire() catch unreachable);
-// defer held.release();
-//
-// const gop = try self.os_data.file_table.getOrPut(resolved_path);
-// if (gop.found_existing) {
-// const prev_value = gop.kv.value.value_ptr.*;
-// cancel gop.kv.value.putter;
-// gop.kv.value = put_data;
-// break :blk prev_value;
-// } else {
-// resolved_path_consumed = true;
-// gop.kv.value = put_data;
-// break :blk null;
-// }
-// };
-//
-// return result;
-// }
-//
-// async fn kqPutEvents(self: *Self, close_op: *CloseOperation, value: V, out_put: **OsData.Put) void {
-// var value_copy = value;
-// var put = OsData.Put{
-// .putter = @frame(),
-// .value_ptr = &value_copy,
-// };
-// out_put.* = &put;
-// self.channel.loop.beginOneEvent();
-//
-// defer {
-// close_op.finish();
-// self.channel.loop.finishOneEvent();
-// }
-//
-// while (true) {
-// if (await (async self.channel.loop.bsdWaitKev(
-// @intCast(usize, close_op.getHandle()),
-// os.EVFILT_VNODE,
-// os.NOTE_WRITE | os.NOTE_DELETE,
-// ) catch unreachable)) |kev| {
-// // TODO handle EV_ERROR
-// if (kev.fflags & os.NOTE_DELETE != 0) {
-// await (async self.channel.put(Self.Event{
-// .id = Event.Id.Delete,
-// .data = value_copy,
-// }) catch unreachable);
-// } else if (kev.fflags & os.NOTE_WRITE != 0) {
-// await (async self.channel.put(Self.Event{
-// .id = Event.Id.CloseWrite,
-// .data = value_copy,
-// }) catch unreachable);
-// }
-// } else |err| switch (err) {
-// error.EventNotFound => unreachable,
-// error.ProcessNotFound => unreachable,
-// error.Overflow => unreachable,
-// error.AccessDenied, error.SystemResources => |casted_err| {
-// await (async self.channel.put(casted_err) catch unreachable);
-// },
-// }
-// }
-// }
-//
-// async fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
-// const value_copy = value;
-//
-// const dirname = std.fs.path.dirname(file_path) orelse ".";
-// const dirname_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, dirname);
-// var dirname_with_null_consumed = false;
-// defer if (!dirname_with_null_consumed) self.channel.loop.allocator.free(dirname_with_null);
-//
-// const basename = std.fs.path.basename(file_path);
-// const basename_with_null = try std.cstr.addNullByte(self.channel.loop.allocator, basename);
-// var basename_with_null_consumed = false;
-// defer if (!basename_with_null_consumed) self.channel.loop.allocator.free(basename_with_null);
-//
-// const wd = try os.inotify_add_watchC(
-// self.os_data.inotify_fd,
-// dirname_with_null.ptr,
-// os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
-// );
-// // wd is either a newly created watch or an existing one.
-//
-// const held = await (async self.os_data.table_lock.acquire() catch unreachable);
-// defer held.release();
-//
-// const gop = try self.os_data.wd_table.getOrPut(wd);
-// if (!gop.found_existing) {
-// gop.kv.value = OsData.Dir{
-// .dirname = dirname_with_null,
-// .file_table = OsData.FileTable.init(self.channel.loop.allocator),
-// };
-// dirname_with_null_consumed = true;
-// }
-// const dir = &gop.kv.value;
-//
-// const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
-// if (file_table_gop.found_existing) {
-// const prev_value = file_table_gop.kv.value;
-// file_table_gop.kv.value = value_copy;
-// return prev_value;
-// } else {
-// file_table_gop.kv.value = value_copy;
-// basename_with_null_consumed = true;
-// return null;
-// }
-// }
-//
-// async fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
-// const value_copy = value;
-// // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
-//
-// const dirname = try std.mem.dupe(self.channel.loop.allocator, u8, std.fs.path.dirname(file_path) orelse ".");
-// var dirname_consumed = false;
-// defer if (!dirname_consumed) self.channel.loop.allocator.free(dirname);
-//
-// const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, dirname);
-// defer self.channel.loop.allocator.free(dirname_utf16le);
-//
-// // TODO https://github.com/ziglang/zig/issues/265
-// const basename = std.fs.path.basename(file_path);
-// const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.channel.loop.allocator, basename);
-// var basename_utf16le_null_consumed = false;
-// defer if (!basename_utf16le_null_consumed) self.channel.loop.allocator.free(basename_utf16le_null);
-// const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
-//
-// const dir_handle = try windows.CreateFileW(
-// dirname_utf16le.ptr,
-// windows.FILE_LIST_DIRECTORY,
-// windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
-// null,
-// windows.OPEN_EXISTING,
-// windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
-// null,
-// );
-// var dir_handle_consumed = false;
-// defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
-//
-// const held = await (async self.os_data.table_lock.acquire() catch unreachable);
-// defer held.release();
-//
-// const gop = try self.os_data.dir_table.getOrPut(dirname);
-// if (gop.found_existing) {
-// const dir = gop.kv.value;
-// const held_dir_lock = await (async dir.table_lock.acquire() catch unreachable);
-// defer held_dir_lock.release();
-//
-// const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
-// if (file_gop.found_existing) {
-// const prev_value = file_gop.kv.value;
-// file_gop.kv.value = value_copy;
-// return prev_value;
-// } else {
-// file_gop.kv.value = value_copy;
-// basename_utf16le_null_consumed = true;
-// return null;
-// }
-// } else {
-// errdefer _ = self.os_data.dir_table.remove(dirname);
-// const dir = try self.channel.loop.allocator.create(OsData.Dir);
-// errdefer self.channel.loop.allocator.destroy(dir);
-//
-// dir.* = OsData.Dir{
-// .file_table = OsData.FileTable.init(self.channel.loop.allocator),
-// .table_lock = event.Lock.init(self.channel.loop),
-// .putter = undefined,
-// };
-// gop.kv.value = dir;
-// assert((try dir.file_table.put(basename_utf16le_no_null, value_copy)) == null);
-// basename_utf16le_null_consumed = true;
-//
-// dir.putter = try async self.windowsDirReader(dir_handle, dir);
-// dir_handle_consumed = true;
-//
-// dirname_consumed = true;
-//
-// return null;
-// }
-// }
-//
-// async fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
-// self.ref();
-// defer self.deref();
-//
-// defer os.close(dir_handle);
-//
-// var putter_node = std.atomic.Queue(anyframe).Node{
-// .data = @frame(),
-// .prev = null,
-// .next = null,
-// };
-// self.os_data.all_putters.put(&putter_node);
-// defer _ = self.os_data.all_putters.remove(&putter_node);
-//
-// var resume_node = Loop.ResumeNode.Basic{
-// .base = Loop.ResumeNode{
-// .id = Loop.ResumeNode.Id.Basic,
-// .handle = @frame(),
-// .overlapped = windows.OVERLAPPED{
-// .Internal = 0,
-// .InternalHigh = 0,
-// .Offset = 0,
-// .OffsetHigh = 0,
-// .hEvent = null,
-// },
-// },
-// };
-// var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
-//
-// // TODO handle this error not in the channel but in the setup
-// _ = windows.CreateIoCompletionPort(
-// dir_handle,
-// self.channel.loop.os_data.io_port,
-// undefined,
-// undefined,
-// ) catch |err| {
-// await (async self.channel.put(err) catch unreachable);
-// return;
-// };
-//
-// while (true) {
-// {
-// // TODO only 1 beginOneEvent for the whole function
-// self.channel.loop.beginOneEvent();
-// errdefer self.channel.loop.finishOneEvent();
-// errdefer {
-// _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);
-// }
-// suspend {
-// _ = windows.kernel32.ReadDirectoryChangesW(
-// dir_handle,
-// &event_buf,
-// @intCast(windows.DWORD, event_buf.len),
-// windows.FALSE, // watch subtree
-// windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
-// windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
-// windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
-// windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
-// null, // number of bytes transferred (unused for async)
-// &resume_node.base.overlapped,
-// null, // completion routine - unused because we use IOCP
-// );
-// }
-// }
-// var bytes_transferred: windows.DWORD = undefined;
-// if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
-// const err = switch (windows.kernel32.GetLastError()) {
-// else => |err| windows.unexpectedError(err),
-// };
-// await (async self.channel.put(err) catch unreachable);
-// } else {
-// // can't use @bytesToSlice because of the special variable length name field
-// var ptr = event_buf[0..].ptr;
-// const end_ptr = ptr + bytes_transferred;
-// var ev: *windows.FILE_NOTIFY_INFORMATION = undefined;
-// while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) {
-// ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr);
-// const emit = switch (ev.Action) {
-// windows.FILE_ACTION_REMOVED => WatchEventId.Delete,
-// windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite,
-// else => null,
-// };
-// if (emit) |id| {
-// const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
-// const user_value = blk: {
-// const held = await (async dir.table_lock.acquire() catch unreachable);
-// defer held.release();
-//
-// if (dir.file_table.get(basename_utf16le)) |entry| {
-// break :blk entry.value;
-// } else {
-// break :blk null;
-// }
-// };
-// if (user_value) |v| {
-// await (async self.channel.put(Event{
-// .id = id,
-// .data = v,
-// }) catch unreachable);
-// }
-// }
-// if (ev.NextEntryOffset == 0) break;
-// }
-// }
-// }
-// }
-//
-// pub async fn removeFile(self: *Self, file_path: []const u8) ?V {
-// @panic("TODO");
-// }
-//
-// async fn linuxEventPutter(inotify_fd: i32, channel: *event.Channel(Event.Error!Event), out_watch: **Self) void {
-// const loop = channel.loop;
-//
-// var watch = Self{
-// .channel = channel,
-// .os_data = OsData{
-// .putter = @frame(),
-// .inotify_fd = inotify_fd,
-// .wd_table = OsData.WdTable.init(loop.allocator),
-// .table_lock = event.Lock.init(loop),
-// },
-// };
-// out_watch.* = &watch;
-//
-// loop.beginOneEvent();
-//
-// defer {
-// watch.os_data.table_lock.deinit();
-// var wd_it = watch.os_data.wd_table.iterator();
-// while (wd_it.next()) |wd_entry| {
-// var file_it = wd_entry.value.file_table.iterator();
-// while (file_it.next()) |file_entry| {
-// loop.allocator.free(file_entry.key);
-// }
-// loop.allocator.free(wd_entry.value.dirname);
-// }
-// loop.finishOneEvent();
-// os.close(inotify_fd);
-// channel.destroy();
-// }
-//
-// var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
-//
-// while (true) {
-// const rc = os.linux.read(inotify_fd, &event_buf, event_buf.len);
-// const errno = os.linux.getErrno(rc);
-// switch (errno) {
-// 0 => {
-// // can't use @bytesToSlice because of the special variable length name field
-// var ptr = event_buf[0..].ptr;
-// const end_ptr = ptr + event_buf.len;
-// var ev: *os.linux.inotify_event = undefined;
-// while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
-// ev = @ptrCast(*os.linux.inotify_event, ptr);
-// if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
-// const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
-// const basename_with_null = basename_ptr[0 .. std.mem.len(u8, basename_ptr) + 1];
-// const user_value = blk: {
-// const held = await (async watch.os_data.table_lock.acquire() catch unreachable);
-// defer held.release();
-//
-// const dir = &watch.os_data.wd_table.get(ev.wd).?.value;
-// if (dir.file_table.get(basename_with_null)) |entry| {
-// break :blk entry.value;
-// } else {
-// break :blk null;
-// }
-// };
-// if (user_value) |v| {
-// await (async channel.put(Event{
-// .id = WatchEventId.CloseWrite,
-// .data = v,
-// }) catch unreachable);
-// }
-// }
-// }
-// },
-// os.linux.EINTR => continue,
-// os.linux.EINVAL => unreachable,
-// os.linux.EFAULT => unreachable,
-// os.linux.EAGAIN => {
-// (await (async loop.linuxWaitFd(
-// inotify_fd,
-// os.linux.EPOLLET | os.linux.EPOLLIN,
-// ) catch unreachable)) catch |err| {
-// const transformed_err = switch (err) {
-// error.FileDescriptorAlreadyPresentInSet => unreachable,
-// error.OperationCausesCircularLoop => unreachable,
-// error.FileDescriptorNotRegistered => unreachable,
-// error.FileDescriptorIncompatibleWithEpoll => unreachable,
-// error.Unexpected => unreachable,
-// else => |e| e,
-// };
-// await (async channel.put(transformed_err) catch unreachable);
-// };
-// },
-// else => unreachable,
-// }
-// }
-// }
-// };
-//}
+pub const WatchEventError = error{
+ UserResourceLimitReached,
+ SystemResources,
+ AccessDenied,
+ Unexpected, // TODO remove this possibility
+};
+
+pub fn Watch(comptime V: type) type {
+ return struct {
+ channel: *event.Channel(Event.Error!Event),
+ os_data: OsData,
+ allocator: *Allocator,
+
+ const OsData = switch (builtin.os) {
+ .macosx, .freebsd, .netbsd, .dragonfly => struct {
+ file_table: FileTable,
+ table_lock: event.Lock,
+
+ const FileTable = std.StringHashMap(*Put);
+ const Put = struct {
+ putter_frame: @Frame(kqPutEvents),
+ cancelled: bool = false,
+ value: V,
+ };
+ },
+
+ .linux => LinuxOsData,
+ .windows => WindowsOsData,
+
+ else => @compileError("Unsupported OS"),
+ };
+
+ const WindowsOsData = struct {
+ table_lock: event.Lock,
+ dir_table: DirTable,
+ all_putters: std.atomic.Queue(Put),
+ ref_count: std.atomic.Int(usize),
+
+ const Put = struct {
+ putter: anyframe,
+ cancelled: bool = false,
+ };
+
+ const DirTable = std.StringHashMap(*Dir);
+ const FileTable = std.HashMap([]const u16, V, hashString, eqlString);
+
+ const Dir = struct {
+ putter_frame: @Frame(windowsDirReader),
+ file_table: FileTable,
+ table_lock: event.Lock,
+ };
+ };
+
+ const LinuxOsData = struct {
+ putter_frame: @Frame(linuxEventPutter),
+ inotify_fd: i32,
+ wd_table: WdTable,
+ table_lock: event.Lock,
+ cancelled: bool = false,
+
+ const WdTable = std.AutoHashMap(i32, Dir);
+ const FileTable = std.StringHashMap(V);
+
+ const Dir = struct {
+ dirname: []const u8,
+ file_table: FileTable,
+ };
+ };
+
+ const Self = @This();
+
+ pub const Event = struct {
+ id: Id,
+ data: V,
+
+ pub const Id = WatchEventId;
+ pub const Error = WatchEventError;
+ };
+
+ pub fn init(allocator: *Allocator, event_buf_count: usize) !*Self {
+ const channel = try allocator.create(event.Channel(Event.Error!Event));
+ errdefer allocator.destroy(channel);
+ var buf = try allocator.alloc(Event.Error!Event, event_buf_count);
+ errdefer allocator.free(buf);
+ channel.init(buf);
+ errdefer channel.deinit();
+
+ const self = try allocator.create(Self);
+ errdefer allocator.destroy(self);
+
+ switch (builtin.os) {
+ .linux => {
+ const inotify_fd = try os.inotify_init1(os.linux.IN_NONBLOCK | os.linux.IN_CLOEXEC);
+ errdefer os.close(inotify_fd);
+
+ self.* = Self{
+ .allocator = allocator,
+ .channel = channel,
+ .os_data = OsData{
+ .putter_frame = undefined,
+ .inotify_fd = inotify_fd,
+ .wd_table = OsData.WdTable.init(allocator),
+ .table_lock = event.Lock.init(),
+ },
+ };
+
+ self.os_data.putter_frame = async self.linuxEventPutter();
+ return self;
+ },
+
+ .windows => {
+ self.* = Self{
+ .allocator = allocator,
+ .channel = channel,
+ .os_data = OsData{
+ .table_lock = event.Lock.init(),
+ .dir_table = OsData.DirTable.init(allocator),
+ .ref_count = std.atomic.Int(usize).init(1),
+ .all_putters = std.atomic.Queue(anyframe).init(),
+ },
+ };
+ return self;
+ },
+
+ .macosx, .freebsd, .netbsd, .dragonfly => {
+ self.* = Self{
+ .allocator = allocator,
+ .channel = channel,
+ .os_data = OsData{
+ .table_lock = event.Lock.init(),
+ .file_table = OsData.FileTable.init(allocator),
+ },
+ };
+ return self;
+ },
+ else => @compileError("Unsupported OS"),
+ }
+ }
+
+ /// All addFile calls and removeFile calls must have completed.
+ pub fn deinit(self: *Self) void {
+ switch (builtin.os) {
+ .macosx, .freebsd, .netbsd, .dragonfly => {
+ // TODO we need to cancel the frames before destroying the lock
+ self.os_data.table_lock.deinit();
+ var it = self.os_data.file_table.iterator();
+ while (it.next()) |entry| {
+ entry.cancelled = true;
+ await entry.value.putter;
+ self.allocator.free(entry.key);
+ self.allocator.free(entry.value);
+ }
+ self.channel.deinit();
+ self.allocator.destroy(self.channel.buffer_nodes);
+ self.allocator.destroy(self);
+ },
+ .linux => {
+ self.os_data.cancelled = true;
+ await self.os_data.putter_frame;
+ self.allocator.destroy(self);
+ },
+ .windows => {
+ while (self.os_data.all_putters.get()) |putter_node| {
+ putter_node.cancelled = true;
+ await putter_node.frame;
+ }
+ self.deref();
+ },
+ else => @compileError("Unsupported OS"),
+ }
+ }
+
+ fn ref(self: *Self) void {
+ _ = self.os_data.ref_count.incr();
+ }
+
+ fn deref(self: *Self) void {
+ if (self.os_data.ref_count.decr() == 1) {
+ self.os_data.table_lock.deinit();
+ var it = self.os_data.dir_table.iterator();
+ while (it.next()) |entry| {
+ self.allocator.free(entry.key);
+ self.allocator.destroy(entry.value);
+ }
+ self.os_data.dir_table.deinit();
+ self.channel.deinit();
+ self.allocator.destroy(self.channel.buffer_nodes);
+ self.allocator.destroy(self);
+ }
+ }
+
+ pub fn addFile(self: *Self, file_path: []const u8, value: V) !?V {
+ switch (builtin.os) {
+ .macosx, .freebsd, .netbsd, .dragonfly => return addFileKEvent(self, file_path, value),
+ .linux => return addFileLinux(self, file_path, value),
+ .windows => return addFileWindows(self, file_path, value),
+ else => @compileError("Unsupported OS"),
+ }
+ }
+
+ fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
+ const resolved_path = try std.fs.path.resolve(self.allocator, [_][]const u8{file_path});
+ var resolved_path_consumed = false;
+ defer if (!resolved_path_consumed) self.allocator.free(resolved_path);
+
+ var close_op = try CloseOperation.start(self.allocator);
+ var close_op_consumed = false;
+ defer if (!close_op_consumed) close_op.finish();
+
+ const flags = if (comptime std.Target.current.isDarwin()) os.O_SYMLINK | os.O_EVTONLY else 0;
+ const mode = 0;
+ const fd = try openPosix(self.allocator, resolved_path, flags, mode);
+ close_op.setHandle(fd);
+
+ var put = try self.allocator.create(OsData.Put);
+ errdefer self.allocator.destroy(put);
+ put.* = OsData.Put{
+ .value = value,
+ .putter_frame = undefined,
+ };
+ put.putter_frame = async self.kqPutEvents(close_op, put);
+ close_op_consumed = true;
+ errdefer {
+ put.cancelled = true;
+ await put.putter_frame;
+ }
+
+ const result = blk: {
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+
+ const gop = try self.os_data.file_table.getOrPut(resolved_path);
+ if (gop.found_existing) {
+ const prev_value = gop.kv.value.value;
+ await gop.kv.value.putter_frame;
+ gop.kv.value = put;
+ break :blk prev_value;
+ } else {
+ resolved_path_consumed = true;
+ gop.kv.value = put;
+ break :blk null;
+ }
+ };
+
+ return result;
+ }
+
+ fn kqPutEvents(self: *Self, close_op: *CloseOperation, put: *OsData.Put) void {
+ global_event_loop.beginOneEvent();
+
+ defer {
+ close_op.finish();
+ global_event_loop.finishOneEvent();
+ }
+
+ while (!put.cancelled) {
+ if (global_event_loop.bsdWaitKev(
+ @intCast(usize, close_op.getHandle()),
+ os.EVFILT_VNODE,
+ os.NOTE_WRITE | os.NOTE_DELETE,
+ )) |kev| {
+ // TODO handle EV_ERROR
+ if (kev.fflags & os.NOTE_DELETE != 0) {
+ self.channel.put(Self.Event{
+ .id = Event.Id.Delete,
+ .data = put.value,
+ });
+ } else if (kev.fflags & os.NOTE_WRITE != 0) {
+ self.channel.put(Self.Event{
+ .id = Event.Id.CloseWrite,
+ .data = put.value,
+ });
+ }
+ } else |err| switch (err) {
+ error.EventNotFound => unreachable,
+ error.ProcessNotFound => unreachable,
+ error.Overflow => unreachable,
+ error.AccessDenied, error.SystemResources => |casted_err| {
+ self.channel.put(casted_err);
+ },
+ }
+ }
+ }
+
+ fn addFileLinux(self: *Self, file_path: []const u8, value: V) !?V {
+ const dirname = std.fs.path.dirname(file_path) orelse ".";
+ const dirname_with_null = try std.cstr.addNullByte(self.allocator, dirname);
+ var dirname_with_null_consumed = false;
+ defer if (!dirname_with_null_consumed) self.channel.free(dirname_with_null);
+
+ const basename = std.fs.path.basename(file_path);
+ const basename_with_null = try std.cstr.addNullByte(self.allocator, basename);
+ var basename_with_null_consumed = false;
+ defer if (!basename_with_null_consumed) self.allocator.free(basename_with_null);
+
+ const wd = try os.inotify_add_watchC(
+ self.os_data.inotify_fd,
+ dirname_with_null.ptr,
+ os.linux.IN_CLOSE_WRITE | os.linux.IN_ONLYDIR | os.linux.IN_EXCL_UNLINK,
+ );
+ // wd is either a newly created watch or an existing one.
+
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+
+ const gop = try self.os_data.wd_table.getOrPut(wd);
+ if (!gop.found_existing) {
+ gop.kv.value = OsData.Dir{
+ .dirname = dirname_with_null,
+ .file_table = OsData.FileTable.init(self.allocator),
+ };
+ dirname_with_null_consumed = true;
+ }
+ const dir = &gop.kv.value;
+
+ const file_table_gop = try dir.file_table.getOrPut(basename_with_null);
+ if (file_table_gop.found_existing) {
+ const prev_value = file_table_gop.kv.value;
+ file_table_gop.kv.value = value;
+ return prev_value;
+ } else {
+ file_table_gop.kv.value = value;
+ basename_with_null_consumed = true;
+ return null;
+ }
+ }
+
+ fn addFileWindows(self: *Self, file_path: []const u8, value: V) !?V {
+ // TODO we might need to convert dirname and basename to canonical file paths ("short"?)
+ const dirname = try std.mem.dupe(self.allocator, u8, std.fs.path.dirname(file_path) orelse ".");
+ var dirname_consumed = false;
+ defer if (!dirname_consumed) self.allocator.free(dirname);
+
+ const dirname_utf16le = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, dirname);
+ defer self.allocator.free(dirname_utf16le);
+
+ // TODO https://github.com/ziglang/zig/issues/265
+ const basename = std.fs.path.basename(file_path);
+ const basename_utf16le_null = try std.unicode.utf8ToUtf16LeWithNull(self.allocator, basename);
+ var basename_utf16le_null_consumed = false;
+ defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
+ const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
+
+ const dir_handle = try windows.CreateFileW(
+ dirname_utf16le.ptr,
+ windows.FILE_LIST_DIRECTORY,
+ windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
+ null,
+ windows.OPEN_EXISTING,
+ windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
+ null,
+ );
+ var dir_handle_consumed = false;
+ defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
+
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+
+ const gop = try self.os_data.dir_table.getOrPut(dirname);
+ if (gop.found_existing) {
+ const dir = gop.kv.value;
+ const held_dir_lock = dir.table_lock.acquire();
+ defer held_dir_lock.release();
+
+ const file_gop = try dir.file_table.getOrPut(basename_utf16le_no_null);
+ if (file_gop.found_existing) {
+ const prev_value = file_gop.kv.value;
+ file_gop.kv.value = value;
+ return prev_value;
+ } else {
+ file_gop.kv.value = value;
+ basename_utf16le_null_consumed = true;
+ return null;
+ }
+ } else {
+ errdefer _ = self.os_data.dir_table.remove(dirname);
+ const dir = try self.allocator.create(OsData.Dir);
+ errdefer self.allocator.destroy(dir);
+
+ dir.* = OsData.Dir{
+ .file_table = OsData.FileTable.init(self.allocator),
+ .table_lock = event.Lock.init(),
+ .putter_frame = undefined,
+ };
+ gop.kv.value = dir;
+ assert((try dir.file_table.put(basename_utf16le_no_null, value)) == null);
+ basename_utf16le_null_consumed = true;
+
+ dir.putter_frame = async self.windowsDirReader(dir_handle, dir);
+ dir_handle_consumed = true;
+
+ dirname_consumed = true;
+
+ return null;
+ }
+ }
+
+ fn windowsDirReader(self: *Self, dir_handle: windows.HANDLE, dir: *OsData.Dir) void {
+ self.ref();
+ defer self.deref();
+
+ defer os.close(dir_handle);
+
+ var putter_node = std.atomic.Queue(anyframe).Node{
+ .data = .{ .putter = @frame() },
+ .prev = null,
+ .next = null,
+ };
+ self.os_data.all_putters.put(&putter_node);
+ defer _ = self.os_data.all_putters.remove(&putter_node);
+
+ var resume_node = Loop.ResumeNode.Basic{
+ .base = Loop.ResumeNode{
+ .id = Loop.ResumeNode.Id.Basic,
+ .handle = @frame(),
+ .overlapped = windows.OVERLAPPED{
+ .Internal = 0,
+ .InternalHigh = 0,
+ .Offset = 0,
+ .OffsetHigh = 0,
+ .hEvent = null,
+ },
+ },
+ };
+ var event_buf: [4096]u8 align(@alignOf(windows.FILE_NOTIFY_INFORMATION)) = undefined;
+
+ // TODO handle this error not in the channel but in the setup
+ _ = windows.CreateIoCompletionPort(
+ dir_handle,
+ global_event_loop.os_data.io_port,
+ undefined,
+ undefined,
+ ) catch |err| {
+ self.channel.put(err);
+ return;
+ };
+
+ while (!putter_node.data.cancelled) {
+ {
+ // TODO only 1 beginOneEvent for the whole function
+ global_event_loop.beginOneEvent();
+ errdefer global_event_loop.finishOneEvent();
+ errdefer {
+ _ = windows.kernel32.CancelIoEx(dir_handle, &resume_node.base.overlapped);
+ }
+ suspend {
+ _ = windows.kernel32.ReadDirectoryChangesW(
+ dir_handle,
+ &event_buf,
+ @intCast(windows.DWORD, event_buf.len),
+ windows.FALSE, // watch subtree
+ windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME |
+ windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | windows.FILE_NOTIFY_CHANGE_SIZE |
+ windows.FILE_NOTIFY_CHANGE_LAST_WRITE | windows.FILE_NOTIFY_CHANGE_LAST_ACCESS |
+ windows.FILE_NOTIFY_CHANGE_CREATION | windows.FILE_NOTIFY_CHANGE_SECURITY,
+ null, // number of bytes transferred (unused for async)
+ &resume_node.base.overlapped,
+ null, // completion routine - unused because we use IOCP
+ );
+ }
+ }
+ var bytes_transferred: windows.DWORD = undefined;
+ if (windows.kernel32.GetOverlappedResult(dir_handle, &resume_node.base.overlapped, &bytes_transferred, windows.FALSE) == 0) {
+ const err = switch (windows.kernel32.GetLastError()) {
+ else => |err| windows.unexpectedError(err),
+ };
+ self.channel.put(err);
+ } else {
+ // can't use @bytesToSlice because of the special variable length name field
+ var ptr = event_buf[0..].ptr;
+ const end_ptr = ptr + bytes_transferred;
+ var ev: *windows.FILE_NOTIFY_INFORMATION = undefined;
+ while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += ev.NextEntryOffset) {
+ ev = @ptrCast(*windows.FILE_NOTIFY_INFORMATION, ptr);
+ const emit = switch (ev.Action) {
+ windows.FILE_ACTION_REMOVED => WatchEventId.Delete,
+ windows.FILE_ACTION_MODIFIED => WatchEventId.CloseWrite,
+ else => null,
+ };
+ if (emit) |id| {
+ const basename_utf16le = ([*]u16)(&ev.FileName)[0 .. ev.FileNameLength / 2];
+ const user_value = blk: {
+ const held = dir.table_lock.acquire();
+ defer held.release();
+
+ if (dir.file_table.get(basename_utf16le)) |entry| {
+ break :blk entry.value;
+ } else {
+ break :blk null;
+ }
+ };
+ if (user_value) |v| {
+ self.channel.put(Event{
+ .id = id,
+ .data = v,
+ });
+ }
+ }
+ if (ev.NextEntryOffset == 0) break;
+ }
+ }
+ }
+ }
+
+ pub fn removeFile(self: *Self, file_path: []const u8) ?V {
+ @panic("TODO");
+ }
+
+ fn linuxEventPutter(self: *Self) void {
+ global_event_loop.beginOneEvent();
+
+ defer {
+ self.os_data.table_lock.deinit();
+ var wd_it = self.os_data.wd_table.iterator();
+ while (wd_it.next()) |wd_entry| {
+ var file_it = wd_entry.value.file_table.iterator();
+ while (file_it.next()) |file_entry| {
+ self.allocator.free(file_entry.key);
+ }
+ self.allocator.free(wd_entry.value.dirname);
+ wd_entry.value.file_table.deinit();
+ }
+ self.os_data.wd_table.deinit();
+ global_event_loop.finishOneEvent();
+ os.close(self.os_data.inotify_fd);
+ self.channel.deinit();
+ self.allocator.free(self.channel.buffer_nodes);
+ }
+
+ var event_buf: [4096]u8 align(@alignOf(os.linux.inotify_event)) = undefined;
+
+ while (!self.os_data.cancelled) {
+ const rc = os.linux.read(self.os_data.inotify_fd, &event_buf, event_buf.len);
+ const errno = os.linux.getErrno(rc);
+ switch (errno) {
+ 0 => {
+ // can't use @bytesToSlice because of the special variable length name field
+ var ptr = event_buf[0..].ptr;
+ const end_ptr = ptr + event_buf.len;
+ var ev: *os.linux.inotify_event = undefined;
+ while (@ptrToInt(ptr) < @ptrToInt(end_ptr)) : (ptr += @sizeOf(os.linux.inotify_event) + ev.len) {
+ ev = @ptrCast(*os.linux.inotify_event, ptr);
+ if (ev.mask & os.linux.IN_CLOSE_WRITE == os.linux.IN_CLOSE_WRITE) {
+ const basename_ptr = ptr + @sizeOf(os.linux.inotify_event);
+ // `ev.len` counts all bytes in `ev.name` including terminating null byte.
+ const basename_with_null = basename_ptr[0..ev.len];
+ const user_value = blk: {
+ const held = self.os_data.table_lock.acquire();
+ defer held.release();
+
+ const dir = &self.os_data.wd_table.get(ev.wd).?.value;
+ if (dir.file_table.get(basename_with_null)) |entry| {
+ break :blk entry.value;
+ } else {
+ break :blk null;
+ }
+ };
+ if (user_value) |v| {
+ self.channel.put(Event{
+ .id = WatchEventId.CloseWrite,
+ .data = v,
+ });
+ }
+ }
+ }
+ },
+ os.linux.EINTR => continue,
+ os.linux.EINVAL => unreachable,
+ os.linux.EFAULT => unreachable,
+ os.linux.EAGAIN => {
+ global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN);
+ },
+ else => unreachable,
+ }
+ }
+ }
+ };
+}
const test_tmp_dir = "std_event_fs_test";
-// TODO this test is disabled until the async function rewrite is finished.
-//test "write a file, watch it, write it again" {
-// return error.SkipZigTest;
-// const allocator = std.heap.direct_allocator;
-//
-// // TODO move this into event loop too
-// try os.makePath(allocator, test_tmp_dir);
-// defer os.deleteTree(test_tmp_dir) catch {};
-//
-// var loop: Loop = undefined;
-// try loop.initMultiThreaded(allocator);
-// defer loop.deinit();
-//
-// var result: anyerror!void = error.ResultNeverWritten;
-// const handle = try async<allocator> testFsWatchCantFail(&loop, &result);
-// defer cancel handle;
-//
-// loop.run();
-// return result;
-//}
-
-fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
- result.* = testFsWatch(loop);
+test "write a file, watch it, write it again" {
+ // TODO provide a way to run tests in evented I/O mode
+ if (!std.io.is_async) return error.SkipZigTest;
+
+ const allocator = std.heap.direct_allocator;
+
+ // TODO move this into event loop too
+ try os.makePath(allocator, test_tmp_dir);
+ defer os.deleteTree(test_tmp_dir) catch {};
+
+ return testFsWatch(&allocator);
}
-fn testFsWatch(loop: *Loop) !void {
- const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
- defer loop.allocator.free(file_path);
+fn testFsWatch(allocator: *Allocator) !void {
+ const file_path = try std.fs.path.join(allocator, [_][]const u8{ test_tmp_dir, "file.txt" });
+ defer allocator.free(file_path);
const contents =
\\line 1
@@ -1350,27 +1326,27 @@ fn testFsWatch(loop: *Loop) !void {
const line2_offset = 7;
// first just write then read the file
- try writeFile(loop, file_path, contents);
+ try writeFile(allocator, file_path, contents);
- const read_contents = try readFile(loop, file_path, 1024 * 1024);
+ const read_contents = try readFile(allocator, file_path, 1024 * 1024);
testing.expectEqualSlices(u8, contents, read_contents);
// now watch the file
- var watch = try Watch(void).create(loop, 0);
- defer watch.destroy();
+ var watch = try Watch(void).init(allocator, 0);
+ defer watch.deinit();
testing.expect((try watch.addFile(file_path, {})) == null);
- const ev = async watch.channel.get();
+ const ev = watch.channel.get();
var ev_consumed = false;
defer if (!ev_consumed) await ev;
// overwrite line 2
- const fd = try await openReadWrite(loop, file_path, File.default_mode);
+ const fd = try await openReadWrite(file_path, File.default_mode);
{
defer os.close(fd);
- try pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);
+ try pwritev(allocator, fd, []const []const u8{"lorem ipsum"}, line2_offset);
}
ev_consumed = true;
@@ -1378,7 +1354,7 @@ fn testFsWatch(loop: *Loop) !void {
WatchEventId.CloseWrite => {},
WatchEventId.Delete => @panic("wrong event"),
}
- const contents_updated = try readFile(loop, file_path, 1024 * 1024);
+ const contents_updated = try readFile(allocator, file_path, 1024 * 1024);
testing.expectEqualSlices(u8,
\\line 1
\\lorem ipsum
@@ -1390,16 +1366,15 @@ fn testFsWatch(loop: *Loop) !void {
pub const OutStream = struct {
fd: fd_t,
stream: Stream,
- loop: *Loop,
+ allocator: *Allocator,
offset: usize,
pub const Error = File.WriteError;
pub const Stream = event.io.OutStream(Error);
- pub fn init(loop: *Loop, fd: fd_t, offset: usize) OutStream {
+ pub fn init(allocator: *Allocator, fd: fd_t, offset: usize) OutStream {
return OutStream{
.fd = fd,
- .loop = loop,
.offset = offset,
.stream = Stream{ .writeFn = writeFn },
};
@@ -1409,23 +1384,22 @@ pub const OutStream = struct {
const self = @fieldParentPtr(OutStream, "stream", out_stream);
const offset = self.offset;
self.offset += bytes.len;
- return pwritev(self.loop, self.fd, [][]const u8{bytes}, offset);
+ return pwritev(self.allocator, self.fd, [_][]const u8{bytes}, offset);
}
};
pub const InStream = struct {
fd: fd_t,
stream: Stream,
- loop: *Loop,
+ allocator: *Allocator,
offset: usize,
pub const Error = PReadVError; // TODO make this not have OutOfMemory
pub const Stream = event.io.InStream(Error);
- pub fn init(loop: *Loop, fd: fd_t, offset: usize) InStream {
+ pub fn init(allocator: *Allocator, fd: fd_t, offset: usize) InStream {
return InStream{
.fd = fd,
- .loop = loop,
.offset = offset,
.stream = Stream{ .readFn = readFn },
};
@@ -1433,7 +1407,7 @@ pub const InStream = struct {
fn readFn(in_stream: *Stream, bytes: []u8) Error!usize {
const self = @fieldParentPtr(InStream, "stream", in_stream);
- const amt = try preadv(self.loop, self.fd, [][]u8{bytes}, self.offset);
+ const amt = try preadv(self.allocator, self.fd, [_][]u8{bytes}, self.offset);
self.offset += amt;
return amt;
}
diff --git a/lib/std/fifo.zig b/lib/std/fifo.zig
index 12d1750df3..e078abcb2b 100644
--- a/lib/std/fifo.zig
+++ b/lib/std/fifo.zig
@@ -148,7 +148,7 @@ pub fn LinearFifo(
var start = self.head + offset;
if (start >= self.buf.len) {
start -= self.buf.len;
- return self.buf[start..self.count - offset];
+ return self.buf[start .. self.count - offset];
} else {
const end = math.min(self.head + self.count, self.buf.len);
return self.buf[start..end];
diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig
index 706c699a87..669321c0f5 100644
--- a/lib/std/mutex.zig
+++ b/lib/std/mutex.zig
@@ -74,7 +74,7 @@ else
pub fn release(self: Held) void {
switch (@atomicRmw(State, &self.mutex.state, .Xchg, .Unlocked, .Release)) {
.Locked => {},
- .Sleeping => self.mutex.parker.unpark(@ptrCast(*const u32, &self.mutex.state)),
+ .Sleeping => self.mutex.parker.unpark(@ptrCast(*const u32, &self.mutex.state)),
.Unlocked => unreachable, // unlocking an unlocked mutex
else => unreachable, // should never be anything else
}
@@ -112,7 +112,7 @@ else
if (@atomicRmw(State, &self.state, .Xchg, .Sleeping, .Acquire) == .Unlocked)
return Held{ .mutex = self };
state = .Sleeping;
- self.parker.park(@ptrCast(*const u32, &self.state), @enumToInt(State.Sleeping));
+ self.parker.park(@ptrCast(*const u32, &self.state), @enumToInt(State.Sleeping));
}
}
};
diff --git a/lib/std/net.zig b/lib/std/net.zig
index 9a602c0105..7a7b2de026 100644
--- a/lib/std/net.zig
+++ b/lib/std/net.zig
@@ -1362,7 +1362,7 @@ pub const StreamServer = struct {
pub const Connection = struct {
file: fs.File,
- address: Address
+ address: Address,
};
/// If this function succeeds, the returned `Connection` is a caller-managed resource.
diff --git a/lib/std/os.zig b/lib/std/os.zig
index cda65b7ea7..7fcdb7f3ff 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -2753,8 +2753,8 @@ pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
/// Used to convert a slice to a null terminated slice on the stack.
/// TODO https://github.com/ziglang/zig/issues/287
-pub fn toPosixPath(file_path: []const u8) ![PATH_MAX-1:0]u8 {
- var path_with_null: [PATH_MAX-1:0]u8 = undefined;
+pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 {
+ var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
// >= rather than > to make room for the null byte
if (file_path.len >= PATH_MAX) return error.NameTooLong;
mem.copy(u8, &path_with_null, file_path);
diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig
index 3cafb8ba0b..118223d03c 100644
--- a/lib/std/os/bits/linux.zig
+++ b/lib/std/os/bits/linux.zig
@@ -1124,7 +1124,6 @@ pub const io_uring_params = extern struct {
pub const IORING_FEAT_SINGLE_MMAP = 1 << 0;
-
// io_uring_params.flags
/// io_context is polled
diff --git a/lib/std/os/uefi/protocols/hii.zig b/lib/std/os/uefi/protocols/hii.zig
index 5dd9095d14..55326b25b3 100644
--- a/lib/std/os/uefi/protocols/hii.zig
+++ b/lib/std/os/uefi/protocols/hii.zig
@@ -26,6 +26,7 @@ pub const HIIPackageHeader = packed struct {
/// The header found at the start of each package list.
pub const HIIPackageList = extern struct {
package_list_guid: Guid,
+
/// The size of the package list (in bytes), including the header.
package_list_length: u32,
diff --git a/lib/std/os/uefi/status.zig b/lib/std/os/uefi/status.zig
index 7c7f98b450..171f3cb05c 100644
--- a/lib/std/os/uefi/status.zig
+++ b/lib/std/os/uefi/status.zig
@@ -5,82 +5,120 @@ pub const success: usize = 0;
/// The image failed to load.
pub const load_error: usize = high_bit | 1;
+
/// A parameter was incorrect.
pub const invalid_parameter: usize = high_bit | 2;
+
/// The operation is not supported.
pub const unsupported: usize = high_bit | 3;
+
/// The buffer was not the proper size for the request.
pub const bad_buffer_size: usize = high_bit | 4;
+
/// The buffer is not large enough to hold the requested data. The required buffer size is returned in the appropriate parameter when this error occurs.
pub const buffer_too_small: usize = high_bit | 5;
+
/// There is no data pending upon return.
pub const not_ready: usize = high_bit | 6;
+
/// The physical device reported an error while attempting the operation.
pub const device_error: usize = high_bit | 7;
+
/// The device cannot be written to.
pub const write_protected: usize = high_bit | 8;
+
/// A resource has run out.
pub const out_of_resources: usize = high_bit | 9;
+
/// An inconstancy was detected on the file system causing the operating to fail.
pub const volume_corrupted: usize = high_bit | 10;
+
/// There is no more space on the file system.
pub const volume_full: usize = high_bit | 11;
+
/// The device does not contain any medium to perform the operation.
pub const no_media: usize = high_bit | 12;
+
/// The medium in the device has changed since the last access.
pub const media_changed: usize = high_bit | 13;
+
/// The item was not found.
pub const not_found: usize = high_bit | 14;
+
/// Access was denied.
pub const access_denied: usize = high_bit | 15;
+
/// The server was not found or did not respond to the request.
pub const no_response: usize = high_bit | 16;
+
/// A mapping to a device does not exist.
pub const no_mapping: usize = high_bit | 17;
+
/// The timeout time expired.
pub const timeout: usize = high_bit | 18;
+
/// The protocol has not been started.
pub const not_started: usize = high_bit | 19;
+
/// The protocol has already been started.
pub const already_started: usize = high_bit | 20;
+
/// The operation was aborted.
pub const aborted: usize = high_bit | 21;
+
/// An ICMP error occurred during the network operation.
pub const icmp_error: usize = high_bit | 22;
+
/// A TFTP error occurred during the network operation.
pub const tftp_error: usize = high_bit | 23;
+
/// A protocol error occurred during the network operation.
pub const protocol_error: usize = high_bit | 24;
+
/// The function encountered an internal version that was incompatible with a version requested by the caller.
pub const incompatible_version: usize = high_bit | 25;
+
/// The function was not performed due to a security violation.
pub const security_violation: usize = high_bit | 26;
+
/// A CRC error was detected.
pub const crc_error: usize = high_bit | 27;
+
/// Beginning or end of media was reached
pub const end_of_media: usize = high_bit | 28;
+
/// The end of the file was reached.
pub const end_of_file: usize = high_bit | 31;
+
/// The language specified was invalid.
pub const invalid_language: usize = high_bit | 32;
+
/// The security status of the data is unknown or compromised and the data must be updated or replaced to restore a valid security status.
pub const compromised_data: usize = high_bit | 33;
+
/// There is an address conflict address allocation
pub const ip_address_conflict: usize = high_bit | 34;
+
/// A HTTP error occurred during the network operation.
pub const http_error: usize = high_bit | 35;
/// The string contained one or more characters that the device could not render and were skipped.
pub const warn_unknown_glyph: usize = 1;
+
/// The handle was closed, but the file was not deleted.
pub const warn_delete_failure: usize = 2;
+
/// The handle was closed, but the data to the file was not flushed properly.
pub const warn_write_failure: usize = 3;
+
/// The resulting buffer was too small, and the data was truncated to the buffer size.
pub const warn_buffer_too_small: usize = 4;
+
/// The data has not been updated within the timeframe set by localpolicy for this type of data.
pub const warn_stale_data: usize = 5;
+
/// The resulting buffer contains UEFI-compliant file system.
pub const warn_file_system: usize = 6;
+
/// The operation will be processed across a system reset.
pub const warn_reset_required: usize = 7;
diff --git a/lib/std/os/uefi/tables/table_header.zig b/lib/std/os/uefi/tables/table_header.zig
index 527c7a61b9..d5d4094232 100644
--- a/lib/std/os/uefi/tables/table_header.zig
+++ b/lib/std/os/uefi/tables/table_header.zig
@@ -1,6 +1,7 @@
pub const TableHeader = extern struct {
signature: u64,
revision: u32,
+
/// The size, in bytes, of the entire table including the TableHeader
header_size: u32,
crc32: u32,
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index 4bf86bc4fd..5fc18accb8 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -192,7 +192,7 @@ pub const FindFirstFileError = error{
};
pub fn FindFirstFile(dir_path: []const u8, find_file_data: *WIN32_FIND_DATAW) FindFirstFileError!HANDLE {
- const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, [_]u16{ '\\', '*'});
+ const dir_path_w = try sliceToPrefixedSuffixedFileW(dir_path, [_]u16{ '\\', '*' });
const handle = kernel32.FindFirstFileW(&dir_path_w, find_file_data);
if (handle == INVALID_HANDLE_VALUE) {
@@ -932,7 +932,7 @@ pub fn wToPrefixedFileW(s: []const u16) ![PATH_MAX_WIDE:0]u16 {
// TODO https://github.com/ziglang/zig/issues/2765
var result: [PATH_MAX_WIDE:0]u16 = undefined;
- const start_index = if (mem.startsWith(u16, s, [_]u16{'\\', '?'})) 0 else blk: {
+ const start_index = if (mem.startsWith(u16, s, [_]u16{ '\\', '?' })) 0 else blk: {
const prefix = [_]u16{ '\\', '?', '?', '\\' };
mem.copy(u16, result[0..], prefix);
break :blk prefix.len;
@@ -942,7 +942,6 @@ pub fn wToPrefixedFileW(s: []const u16) ![PATH_MAX_WIDE:0]u16 {
mem.copy(u16, result[start_index..], s);
result[end_index] = 0;
return result;
-
}
pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) ![PATH_MAX_WIDE + suffix.len:0]u16 {
diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig
index c34077a9dc..0554f09705 100644
--- a/lib/std/os/windows/ws2_32.zig
+++ b/lib/std/os/windows/ws2_32.zig
@@ -106,12 +106,7 @@ pub const WSAOVERLAPPED = extern struct {
hEvent: ?WSAEVENT,
};
-pub const WSAOVERLAPPED_COMPLETION_ROUTINE = extern fn (
- dwError: DWORD,
- cbTransferred: DWORD,
- lpOverlapped: *WSAOVERLAPPED,
- dwFlags: DWORD
-) void;
+pub const WSAOVERLAPPED_COMPLETION_ROUTINE = extern fn (dwError: DWORD, cbTransferred: DWORD, lpOverlapped: *WSAOVERLAPPED, dwFlags: DWORD) void;
pub const WSA_INVALID_HANDLE = 6;
pub const WSA_NOT_ENOUGH_MEMORY = 8;
@@ -209,11 +204,12 @@ pub const WSA_QOS_ESDMODEOBJ = 11029;
pub const WSA_QOS_ESHAPERATEOBJ = 11030;
pub const WSA_QOS_RESERVED_PETYPE = 11031;
-
/// no parameters
const IOC_VOID = 0x80000000;
+
/// copy out parameters
const IOC_OUT = 0x40000000;
+
/// copy in parameters
const IOC_IN = 0x80000000;
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 9f9064f88d..63c6168ed9 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -2204,7 +2204,7 @@ pub const Node = struct {
};
pub const VarType = struct {
- base: Node,
+ base: Node = Node{ .id = .VarType },
token: TokenIndex,
pub fn iterate(self: *VarType, index: usize) ?*Node {
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index fd15cda11a..3ece8d150b 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -410,10 +410,16 @@ fn parseContainerField(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
var align_expr: ?*Node = null;
var type_expr: ?*Node = null;
if (eatToken(it, .Colon)) |_| {
- type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
- .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
- });
- align_expr = try parseByteAlign(arena, it, tree);
+ if (eatToken(it, .Keyword_var)) |var_tok| {
+ const node = try arena.create(ast.Node.VarType);
+ node.* = .{ .token = var_tok };
+ type_expr = &node.base;
+ } else {
+ type_expr = try expectNode(arena, it, tree, parseTypeExpr, AstError{
+ .ExpectedTypeExpr = AstError.ExpectedTypeExpr{ .token = it.index },
+ });
+ align_expr = try parseByteAlign(arena, it, tree);
+ }
}
const value_expr = if (eatToken(it, .Equal)) |_|
@@ -576,7 +582,8 @@ fn parseIfStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
/// LabeledStatement <- BlockLabel? (Block / LoopStatement)
fn parseLabeledStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const label_token = parseBlockLabel(arena, it, tree);
+ var colon: TokenIndex = undefined;
+ const label_token = parseBlockLabel(arena, it, tree, &colon);
if (try parseBlock(arena, it, tree)) |node| {
node.cast(Node.Block).?.label = label_token;
@@ -757,7 +764,8 @@ fn parseBlockExprStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !
/// BlockExpr <- BlockLabel? Block
fn parseBlockExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node {
- const label_token = parseBlockLabel(arena, it, tree);
+ var colon: TokenIndex = undefined;
+ const label_token = parseBlockLabel(arena, it, tree, &colon);
const block_node = (try parseBlock(arena, it, tree)) orelse {
if (label_token) |label| {
putBackToken(it, label + 1); // ":"
@@ -913,7 +921,8 @@ fn parsePrimaryExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
return &node.base;
}
- const label = parseBlockLabel(arena, it, tree);
+ var colon: TokenIndex = undefined;
+ const label = parseBlockLabel(arena, it, tree, &colon);
if (try parseLoopExpr(arena, it, tree)) |node| {
if (node.cast(Node.For)) |for_node| {
for_node.label = label;
@@ -1354,7 +1363,8 @@ fn parseIfTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
/// <- BlockLabel Block
/// / BlockLabel? LoopTypeExpr
fn parseLabeledTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
- const label = parseBlockLabel(arena, it, tree);
+ var colon: TokenIndex = undefined;
+ const label = parseBlockLabel(arena, it, tree, &colon);
if (label) |token| {
if (try parseBlock(arena, it, tree)) |node| {
@@ -1372,12 +1382,9 @@ fn parseLabeledTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
return node;
}
- if (label != null) {
- // If we saw a label, there should have been a block next
- try tree.errors.push(AstError{
- .ExpectedLBrace = AstError.ExpectedLBrace{ .token = it.index },
- });
- return error.ParseError;
+ if (label) |token| {
+ putBackToken(it, colon);
+ putBackToken(it, token);
}
return null;
}
@@ -1641,9 +1648,12 @@ fn parseBreakLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
}
/// BlockLabel <- IDENTIFIER COLON
-fn parseBlockLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree) ?TokenIndex {
+fn parseBlockLabel(arena: *Allocator, it: *TokenIterator, tree: *Tree, colon_token: *TokenIndex) ?TokenIndex {
const identifier = eatToken(it, .Identifier) orelse return null;
- if (eatToken(it, .Colon) != null) return identifier;
+ if (eatToken(it, .Colon)) |colon| {
+ colon_token.* = colon;
+ return identifier;
+ }
putBackToken(it, identifier);
return null;
}
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index ce19588722..0d38ec5ccb 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -1,3 +1,30 @@
+test "zig fmt: var struct field" {
+ try testCanonical(
+ \\pub const Pointer = struct {
+ \\ sentinel: var,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: sentinel-terminated array type" {
+ try testCanonical(
+ \\pub fn cStrToPrefixedFileW(s: [*:0]const u8) ![PATH_MAX_WIDE:0]u16 {
+ \\ return sliceToPrefixedFileW(mem.toSliceConst(u8, s));
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: sentinel-terminated slice type" {
+ try testCanonical(
+ \\pub fn toSlice(self: Buffer) [:0]u8 {
+ \\ return self.list.toSlice()[0..self.len()];
+ \\}
+ \\
+ );
+}
+
test "zig fmt: anon literal in array" {
try testCanonical(
\\var arr: [2]Foo = .{
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index d6e2456455..1b6aaf7102 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -477,7 +477,14 @@ fn renderExpression(
ast.Node.PrefixOp.Op.SliceType => |ptr_info| {
try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [
- try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ]
+ if (ptr_info.sentinel) |sentinel| {
+ const colon_token = tree.prevToken(sentinel.firstToken());
+ try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // :
+ try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
+ try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ]
+ } else {
+ try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ]
+ }
if (ptr_info.allowzero_token) |allowzero_token| {
try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero