authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-07 22:31:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:48-07:00
log47aa5a70a54ef7838e7c8e5ebdc570f07048ec04
tree80c2b9edc39f3b51746e89b7c90a1eab79075ff5
parent066864a0bf59bc1a926412b3c6e4d2d0c65e5642

std: updating to std.Io interface

got the build runner compiling

34 files changed, 805 insertions(+), 564 deletions(-)

lib/compiler/build_runner.zig+18-14
......@@ -1,5 +1,8 @@
1const std = @import("std");
1const runner = @This();
22const builtin = @import("builtin");
3
4const std = @import("std");
5const Io = std.Io;
36const assert = std.debug.assert;
47const fmt = std.fmt;
58const mem = std.mem;
......@@ -11,7 +14,6 @@ const WebServer = std.Build.WebServer;
1114const Allocator = std.mem.Allocator;
1215const fatal = std.process.fatal;
1316const Writer = std.Io.Writer;
14const runner = @This();
1517const tty = std.Io.tty;
1618
1719pub const root = @import("@build");
......@@ -75,6 +77,7 @@ pub fn main() !void {
7577 .io = io,
7678 .arena = arena,
7779 .cache = .{
80 .io = io,
7881 .gpa = arena,
7982 .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),
8083 },
......@@ -84,7 +87,7 @@ pub fn main() !void {
8487 .zig_lib_directory = zig_lib_directory,
8588 .host = .{
8689 .query = .{},
87 .result = try std.zig.system.resolveTargetQuery(.{}),
90 .result = try std.zig.system.resolveTargetQuery(io, .{}),
8891 },
8992 .time_report = false,
9093 };
......@@ -121,7 +124,7 @@ pub fn main() !void {
121124 var watch = false;
122125 var fuzz: ?std.Build.Fuzz.Mode = null;
123126 var debounce_interval_ms: u16 = 50;
124 var webui_listen: ?std.net.Address = null;
127 var webui_listen: ?Io.net.IpAddress = null;
125128
126129 if (try std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(arena)) |str| {
127130 if (std.meta.stringToEnum(ErrorStyle, str)) |style| {
......@@ -288,11 +291,11 @@ pub fn main() !void {
288291 });
289292 };
290293 } else if (mem.eql(u8, arg, "--webui")) {
291 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
294 if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
292295 } else if (mem.startsWith(u8, arg, "--webui=")) {
293296 const addr_str = arg["--webui=".len..];
294297 if (std.mem.eql(u8, addr_str, "-")) fatal("web interface cannot listen on stdio", .{});
295 webui_listen = std.net.Address.parseIpAndPort(addr_str) catch |err| {
298 webui_listen = Io.net.IpAddress.parseLiteral(addr_str) catch |err| {
296299 fatal("invalid web UI address '{s}': {s}", .{ addr_str, @errorName(err) });
297300 };
298301 } else if (mem.eql(u8, arg, "--debug-log")) {
......@@ -334,14 +337,10 @@ pub fn main() !void {
334337 watch = true;
335338 } else if (mem.eql(u8, arg, "--time-report")) {
336339 graph.time_report = true;
337 if (webui_listen == null) {
338 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
339 }
340 if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
340341 } else if (mem.eql(u8, arg, "--fuzz")) {
341342 fuzz = .{ .forever = undefined };
342 if (webui_listen == null) {
343 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
344 }
343 if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
345344 } else if (mem.startsWith(u8, arg, "--fuzz=")) {
346345 const value = arg["--fuzz=".len..];
347346 if (value.len == 0) fatal("missing argument to --fuzz", .{});
......@@ -550,13 +549,15 @@ pub fn main() !void {
550549
551550 var w: Watch = w: {
552551 if (!watch) break :w undefined;
553 if (!Watch.have_impl) fatal("--watch not yet implemented for {s}", .{@tagName(builtin.os.tag)});
552 if (!Watch.have_impl) fatal("--watch not yet implemented for {t}", .{builtin.os.tag});
554553 break :w try .init();
555554 };
556555
557556 try run.thread_pool.init(thread_pool_options);
558557 defer run.thread_pool.deinit();
559558
559 const now = Io.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});
560
560561 run.web_server = if (webui_listen) |listen_address| ws: {
561562 if (builtin.single_threaded) unreachable; // `fatal` above
562563 break :ws .init(.{
......@@ -568,11 +569,12 @@ pub fn main() !void {
568569 .root_prog_node = main_progress_node,
569570 .watch = watch,
570571 .listen_address = listen_address,
572 .base_timestamp = now,
571573 });
572574 } else null;
573575
574576 if (run.web_server) |*ws| {
575 ws.start() catch |err| fatal("failed to start web server: {s}", .{@errorName(err)});
577 ws.start() catch |err| fatal("failed to start web server: {t}", .{err});
576578 }
577579
578580 rebuild: while (true) : (if (run.error_style.clearOnUpdate()) {
......@@ -755,6 +757,7 @@ fn runStepNames(
755757 fuzz: ?std.Build.Fuzz.Mode,
756758) !void {
757759 const gpa = run.gpa;
760 const io = b.graph.io;
758761 const step_stack = &run.step_stack;
759762 const thread_pool = &run.thread_pool;
760763
......@@ -858,6 +861,7 @@ fn runStepNames(
858861 assert(mode == .limit);
859862 var f = std.Build.Fuzz.init(
860863 gpa,
864 io,
861865 thread_pool,
862866 step_stack.keys(),
863867 parent_prog_node,
lib/compiler/test_runner.zig+7-8
......@@ -2,6 +2,7 @@
22const builtin = @import("builtin");
33
44const std = @import("std");
5const Io = std.Io;
56const fatal = std.process.fatal;
67const testing = std.testing;
78const assert = std.debug.assert;
......@@ -16,6 +17,7 @@ var fba: std.heap.FixedBufferAllocator = .init(&fba_buffer);
1617var fba_buffer: [8192]u8 = undefined;
1718var stdin_buffer: [4096]u8 = undefined;
1819var stdout_buffer: [4096]u8 = undefined;
20var runner_threaded_io: Io.Threaded = .init_single_threaded;
1921
2022/// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether
2123/// the test runner will communicate with the build runner via `std.zig.Server`.
......@@ -63,8 +65,6 @@ pub fn main() void {
6365 fuzz_abi.fuzzer_init(.fromSlice(cache_dir));
6466 }
6567
66 fba.reset();
67
6868 if (listen) {
6969 return mainServer() catch @panic("internal test runner failure");
7070 } else {
......@@ -74,7 +74,7 @@ pub fn main() void {
7474
7575fn mainServer() !void {
7676 @disableInstrumentation();
77 var stdin_reader = std.fs.File.stdin().readerStreaming(&stdin_buffer);
77 var stdin_reader = std.fs.File.stdin().readerStreaming(runner_threaded_io.io(), &stdin_buffer);
7878 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
7979 var server = try std.zig.Server.init(.{
8080 .in = &stdin_reader.interface,
......@@ -131,7 +131,7 @@ fn mainServer() !void {
131131
132132 .run_test => {
133133 testing.allocator_instance = .{};
134 testing.io_instance = .init(fba.allocator());
134 testing.io_instance = .init(testing.allocator);
135135 log_err_count = 0;
136136 const index = try server.receiveBody_u32();
137137 const test_fn = builtin.test_functions[index];
......@@ -154,7 +154,6 @@ fn mainServer() !void {
154154 },
155155 };
156156 testing.io_instance.deinit();
157 fba.reset();
158157 const leak_count = testing.allocator_instance.detectLeaks();
159158 testing.allocator_instance.deinitWithoutLeakChecks();
160159 try server.serveTestResults(.{
......@@ -234,10 +233,10 @@ fn mainTerminal() void {
234233 var leaks: usize = 0;
235234 for (test_fn_list, 0..) |test_fn, i| {
236235 testing.allocator_instance = .{};
237 testing.io_instance = .init(fba.allocator());
236 testing.io_instance = .init(testing.allocator);
238237 defer {
239 if (testing.allocator_instance.deinit() == .leak) leaks += 1;
240238 testing.io_instance.deinit();
239 if (testing.allocator_instance.deinit() == .leak) leaks += 1;
241240 }
242241 testing.log_level = .warn;
243242
......@@ -324,7 +323,7 @@ pub fn mainSimple() anyerror!void {
324323 .stage2_aarch64, .stage2_riscv64 => true,
325324 else => false,
326325 };
327 // is the backend capable of calling `std.Io.Writer.print`?
326 // is the backend capable of calling `Io.Writer.print`?
328327 const enable_print = switch (builtin.zig_backend) {
329328 .stage2_aarch64, .stage2_riscv64 => true,
330329 else => false,
lib/std/Build.zig+3-1
......@@ -1837,6 +1837,8 @@ pub fn runAllowFail(
18371837 if (!process.can_spawn)
18381838 return error.ExecNotSupported;
18391839
1840 const io = b.graph.io;
1841
18401842 const max_output_size = 400 * 1024;
18411843 var child = std.process.Child.init(argv, b.allocator);
18421844 child.stdin_behavior = .Ignore;
......@@ -1847,7 +1849,7 @@ pub fn runAllowFail(
18471849 try Step.handleVerbose2(b, null, child.env_map, argv);
18481850 try child.spawn();
18491851
1850 var stdout_reader = child.stdout.?.readerStreaming(&.{});
1852 var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
18511853 const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch {
18521854 return error.ReadFailure;
18531855 };
lib/std/Build/Cache.zig+16-6
......@@ -3,8 +3,10 @@
33//! not to withstand attacks using specially-crafted input.
44
55const Cache = @This();
6const std = @import("std");
76const builtin = @import("builtin");
7
8const std = @import("std");
9const Io = std.Io;
810const crypto = std.crypto;
911const fs = std.fs;
1012const assert = std.debug.assert;
......@@ -15,6 +17,7 @@ const Allocator = std.mem.Allocator;
1517const log = std.log.scoped(.cache);
1618
1719gpa: Allocator,
20io: Io,
1821manifest_dir: fs.Dir,
1922hash: HashHelper = .{},
2023/// This value is accessed from multiple threads, protected by mutex.
......@@ -661,9 +664,10 @@ pub const Manifest = struct {
661664 },
662665 } {
663666 const gpa = self.cache.gpa;
667 const io = self.cache.io;
664668 const input_file_count = self.files.entries.len;
665669 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded
666 var manifest_reader = self.manifest_file.?.reader(&tiny_buffer); // Reads positionally from zero.
670 var manifest_reader = self.manifest_file.?.reader(io, &tiny_buffer); // Reads positionally from zero.
667671 const limit: std.Io.Limit = .limited(manifest_file_size_max);
668672 const file_contents = manifest_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) {
669673 error.OutOfMemory => return error.OutOfMemory,
......@@ -1337,7 +1341,8 @@ test "cache file and then recall it" {
13371341 var digest2: HexDigest = undefined;
13381342
13391343 {
1340 var cache = Cache{
1344 var cache: Cache = .{
1345 .io = io,
13411346 .gpa = testing.allocator,
13421347 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
13431348 };
......@@ -1402,7 +1407,8 @@ test "check that changing a file makes cache fail" {
14021407 var digest2: HexDigest = undefined;
14031408
14041409 {
1405 var cache = Cache{
1410 var cache: Cache = .{
1411 .io = io,
14061412 .gpa = testing.allocator,
14071413 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
14081414 };
......@@ -1451,6 +1457,8 @@ test "check that changing a file makes cache fail" {
14511457}
14521458
14531459test "no file inputs" {
1460 const io = testing.io;
1461
14541462 var tmp = testing.tmpDir(.{});
14551463 defer tmp.cleanup();
14561464
......@@ -1459,7 +1467,8 @@ test "no file inputs" {
14591467 var digest1: HexDigest = undefined;
14601468 var digest2: HexDigest = undefined;
14611469
1462 var cache = Cache{
1470 var cache: Cache = .{
1471 .io = io,
14631472 .gpa = testing.allocator,
14641473 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
14651474 };
......@@ -1517,7 +1526,8 @@ test "Manifest with files added after initial hash work" {
15171526 var digest3: HexDigest = undefined;
15181527
15191528 {
1520 var cache = Cache{
1529 var cache: Cache = .{
1530 .io = io,
15211531 .gpa = testing.allocator,
15221532 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
15231533 };
lib/std/Build/Fuzz.zig+6-1
......@@ -1,4 +1,5 @@
11const std = @import("../std.zig");
2const Io = std.Io;
23const Build = std.Build;
34const Cache = Build.Cache;
45const Step = std.Build.Step;
......@@ -14,6 +15,7 @@ const Fuzz = @This();
1415const build_runner = @import("root");
1516
1617gpa: Allocator,
18io: Io,
1719mode: Mode,
1820
1921/// Allocated into `gpa`.
......@@ -75,6 +77,7 @@ const CoverageMap = struct {
7577
7678pub fn init(
7779 gpa: Allocator,
80 io: Io,
7881 thread_pool: *std.Thread.Pool,
7982 all_steps: []const *Build.Step,
8083 root_prog_node: std.Progress.Node,
......@@ -111,6 +114,7 @@ pub fn init(
111114
112115 return .{
113116 .gpa = gpa,
117 .io = io,
114118 .mode = mode,
115119 .run_steps = run_steps,
116120 .wait_group = .{},
......@@ -484,6 +488,7 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
484488
485489pub fn waitAndPrintReport(fuzz: *Fuzz) void {
486490 assert(fuzz.mode == .limit);
491 const io = fuzz.io;
487492
488493 fuzz.wait_group.wait();
489494 fuzz.wait_group.reset();
......@@ -506,7 +511,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
506511
507512 const fuzz_abi = std.Build.abi.fuzz;
508513 var rbuf: [0x1000]u8 = undefined;
509 var r = coverage_file.reader(&rbuf);
514 var r = coverage_file.reader(io, &rbuf);
510515
511516 var header: fuzz_abi.SeenPcsHeader = undefined;
512517 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {
lib/std/Build/Step.zig+11-8
......@@ -1,9 +1,11 @@
11const Step = @This();
2const builtin = @import("builtin");
3
24const std = @import("../std.zig");
5const Io = std.Io;
36const Build = std.Build;
47const Allocator = std.mem.Allocator;
58const assert = std.debug.assert;
6const builtin = @import("builtin");
79const Cache = Build.Cache;
810const Path = Cache.Path;
911const ArrayList = std.ArrayList;
......@@ -327,7 +329,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
327329}
328330
329331/// For debugging purposes, prints identifying information about this Step.
330pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void {
332pub fn dump(step: *Step, w: *Io.Writer, tty_config: Io.tty.Config) void {
331333 if (step.debug_stack_trace.instruction_addresses.len > 0) {
332334 w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
333335 std.debug.writeStackTrace(&step.debug_stack_trace, w, tty_config) catch {};
......@@ -382,7 +384,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO
382384
383385pub const ZigProcess = struct {
384386 child: std.process.Child,
385 poller: std.Io.Poller(StreamEnum),
387 poller: Io.Poller(StreamEnum),
386388 progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void,
387389
388390 pub const StreamEnum = enum { stdout, stderr };
......@@ -458,7 +460,7 @@ pub fn evalZigProcess(
458460 const zp = try gpa.create(ZigProcess);
459461 zp.* = .{
460462 .child = child,
461 .poller = std.Io.poll(gpa, ZigProcess.StreamEnum, .{
463 .poller = Io.poll(gpa, ZigProcess.StreamEnum, .{
462464 .stdout = child.stdout.?,
463465 .stderr = child.stderr.?,
464466 }),
......@@ -505,11 +507,12 @@ pub fn evalZigProcess(
505507}
506508
507509/// Wrapper around `std.fs.Dir.updateFile` that handles verbose and error output.
508pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus {
510pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !Io.Dir.PrevStatus {
509511 const b = s.owner;
512 const io = b.graph.io;
510513 const src_path = src_lazy_path.getPath3(b, s);
511514 try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });
512 return src_path.root_dir.handle.updateFile(src_path.sub_path, std.fs.cwd(), dest_path, .{}) catch |err| {
515 return Io.Dir.updateFile(src_path.root_dir.handle.adaptToNewApi(), io, src_path.sub_path, .cwd(), dest_path, .{}) catch |err| {
513516 return s.fail("unable to update file from '{f}' to '{s}': {s}", .{
514517 src_path, dest_path, @errorName(err),
515518 });
......@@ -738,7 +741,7 @@ pub fn allocPrintCmd2(
738741 argv: []const []const u8,
739742) Allocator.Error![]u8 {
740743 const shell = struct {
741 fn escape(writer: *std.Io.Writer, string: []const u8, is_argv0: bool) !void {
744 fn escape(writer: *Io.Writer, string: []const u8, is_argv0: bool) !void {
742745 for (string) |c| {
743746 if (switch (c) {
744747 else => true,
......@@ -772,7 +775,7 @@ pub fn allocPrintCmd2(
772775 }
773776 };
774777
775 var aw: std.Io.Writer.Allocating = .init(gpa);
778 var aw: Io.Writer.Allocating = .init(gpa);
776779 defer aw.deinit();
777780 const writer = &aw.writer;
778781 if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory;
lib/std/Build/Step/Options.zig+2
......@@ -538,8 +538,10 @@ test Options {
538538 defer arena.deinit();
539539
540540 var graph: std.Build.Graph = .{
541 .io = io,
541542 .arena = arena.allocator(),
542543 .cache = .{
544 .io = io,
543545 .gpa = arena.allocator(),
544546 .manifest_dir = std.fs.cwd(),
545547 },
lib/std/Build/Step/Run.zig+8-5
......@@ -761,6 +761,7 @@ const IndexedOutput = struct {
761761};
762762fn make(step: *Step, options: Step.MakeOptions) !void {
763763 const b = step.owner;
764 const io = b.graph.io;
764765 const arena = b.allocator;
765766 const run: *Run = @fieldParentPtr("step", step);
766767 const has_side_effects = run.hasSideEffects();
......@@ -834,7 +835,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
834835 defer file.close();
835836
836837 var buf: [1024]u8 = undefined;
837 var file_reader = file.reader(&buf);
838 var file_reader = file.reader(io, &buf);
838839 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
839840 error.ReadFailed => return step.fail(
840841 "failed to read from '{f}': {t}",
......@@ -1067,6 +1068,7 @@ pub fn rerunInFuzzMode(
10671068) !void {
10681069 const step = &run.step;
10691070 const b = step.owner;
1071 const io = b.graph.io;
10701072 const arena = b.allocator;
10711073 var argv_list: std.ArrayList([]const u8) = .empty;
10721074 for (run.argv.items) |arg| {
......@@ -1093,7 +1095,7 @@ pub fn rerunInFuzzMode(
10931095 defer file.close();
10941096
10951097 var buf: [1024]u8 = undefined;
1096 var file_reader = file.reader(&buf);
1098 var file_reader = file.reader(io, &buf);
10971099 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
10981100 error.ReadFailed => return file_reader.err.?,
10991101 error.WriteFailed => return error.OutOfMemory,
......@@ -2090,6 +2092,7 @@ fn sendRunFuzzTestMessage(
20902092
20912093fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
20922094 const b = run.step.owner;
2095 const io = b.graph.io;
20932096 const arena = b.allocator;
20942097
20952098 try child.spawn();
......@@ -2113,7 +2116,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
21132116 defer file.close();
21142117 // TODO https://github.com/ziglang/zig/issues/23955
21152118 var read_buffer: [1024]u8 = undefined;
2116 var file_reader = file.reader(&read_buffer);
2119 var file_reader = file.reader(io, &read_buffer);
21172120 var write_buffer: [1024]u8 = undefined;
21182121 var stdin_writer = child.stdin.?.writer(&write_buffer);
21192122 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
......@@ -2159,7 +2162,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
21592162 stdout_bytes = try poller.toOwnedSlice(.stdout);
21602163 stderr_bytes = try poller.toOwnedSlice(.stderr);
21612164 } else {
2162 var stdout_reader = stdout.readerStreaming(&.{});
2165 var stdout_reader = stdout.readerStreaming(io, &.{});
21632166 stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
21642167 error.OutOfMemory => return error.OutOfMemory,
21652168 error.ReadFailed => return stdout_reader.err.?,
......@@ -2167,7 +2170,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
21672170 };
21682171 }
21692172 } else if (child.stderr) |stderr| {
2170 var stderr_reader = stderr.readerStreaming(&.{});
2173 var stderr_reader = stderr.readerStreaming(io, &.{});
21712174 stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
21722175 error.OutOfMemory => return error.OutOfMemory,
21732176 error.ReadFailed => return stderr_reader.err.?,
lib/std/Build/Step/UpdateSourceFiles.zig+13-11
......@@ -3,11 +3,13 @@
33//! not be used during the normal build process, but as a utility run by a
44//! developer with intention to update source files, which will then be
55//! committed to version control.
6const UpdateSourceFiles = @This();
7
68const std = @import("std");
9const Io = std.Io;
710const Step = std.Build.Step;
811const fs = std.fs;
912const ArrayList = std.ArrayList;
10const UpdateSourceFiles = @This();
1113
1214step: Step,
1315output_source_files: std.ArrayListUnmanaged(OutputSourceFile),
......@@ -70,22 +72,21 @@ pub fn addBytesToSource(usf: *UpdateSourceFiles, bytes: []const u8, sub_path: []
7072fn make(step: *Step, options: Step.MakeOptions) !void {
7173 _ = options;
7274 const b = step.owner;
75 const io = b.graph.io;
7376 const usf: *UpdateSourceFiles = @fieldParentPtr("step", step);
7477
7578 var any_miss = false;
7679 for (usf.output_source_files.items) |output_source_file| {
7780 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
7881 b.build_root.handle.makePath(dirname) catch |err| {
79 return step.fail("unable to make path '{f}{s}': {s}", .{
80 b.build_root, dirname, @errorName(err),
81 });
82 return step.fail("unable to make path '{f}{s}': {t}", .{ b.build_root, dirname, err });
8283 };
8384 }
8485 switch (output_source_file.contents) {
8586 .bytes => |bytes| {
8687 b.build_root.handle.writeFile(.{ .sub_path = output_source_file.sub_path, .data = bytes }) catch |err| {
87 return step.fail("unable to write file '{f}{s}': {s}", .{
88 b.build_root, output_source_file.sub_path, @errorName(err),
88 return step.fail("unable to write file '{f}{s}': {t}", .{
89 b.build_root, output_source_file.sub_path, err,
8990 });
9091 };
9192 any_miss = true;
......@@ -94,15 +95,16 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
9495 if (!step.inputs.populated()) try step.addWatchInput(file_source);
9596
9697 const source_path = file_source.getPath2(b, step);
97 const prev_status = fs.Dir.updateFile(
98 fs.cwd(),
98 const prev_status = Io.Dir.updateFile(
99 .cwd(),
100 io,
99101 source_path,
100 b.build_root.handle,
102 b.build_root.handle.adaptToNewApi(),
101103 output_source_file.sub_path,
102104 .{},
103105 ) catch |err| {
104 return step.fail("unable to update file from '{s}' to '{f}{s}': {s}", .{
105 source_path, b.build_root, output_source_file.sub_path, @errorName(err),
106 return step.fail("unable to update file from '{s}' to '{f}{s}': {t}", .{
107 source_path, b.build_root, output_source_file.sub_path, err,
106108 });
107109 };
108110 any_miss = any_miss or prev_status == .stale;
lib/std/Build/Step/WriteFile.zig+13-23
......@@ -2,6 +2,7 @@
22//! the local cache which has a set of files that have either been generated
33//! during the build, or are copied from the source package.
44const std = @import("std");
5const Io = std.Io;
56const Step = std.Build.Step;
67const fs = std.fs;
78const ArrayList = std.ArrayList;
......@@ -174,6 +175,7 @@ fn maybeUpdateName(write_file: *WriteFile) void {
174175fn make(step: *Step, options: Step.MakeOptions) !void {
175176 _ = options;
176177 const b = step.owner;
178 const io = b.graph.io;
177179 const arena = b.allocator;
178180 const gpa = arena;
179181 const write_file: *WriteFile = @fieldParentPtr("step", step);
......@@ -264,40 +266,27 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
264266 };
265267 defer cache_dir.close();
266268
267 const cwd = fs.cwd();
268
269269 for (write_file.files.items) |file| {
270270 if (fs.path.dirname(file.sub_path)) |dirname| {
271271 cache_dir.makePath(dirname) catch |err| {
272 return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{
273 b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err),
272 return step.fail("unable to make path '{f}{s}{c}{s}': {t}", .{
273 b.cache_root, cache_path, fs.path.sep, dirname, err,
274274 });
275275 };
276276 }
277277 switch (file.contents) {
278278 .bytes => |bytes| {
279279 cache_dir.writeFile(.{ .sub_path = file.sub_path, .data = bytes }) catch |err| {
280 return step.fail("unable to write file '{f}{s}{c}{s}': {s}", .{
281 b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err),
280 return step.fail("unable to write file '{f}{s}{c}{s}': {t}", .{
281 b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
282282 });
283283 };
284284 },
285285 .copy => |file_source| {
286286 const source_path = file_source.getPath2(b, step);
287 const prev_status = fs.Dir.updateFile(
288 cwd,
289 source_path,
290 cache_dir,
291 file.sub_path,
292 .{},
293 ) catch |err| {
294 return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {s}", .{
295 source_path,
296 b.cache_root,
297 cache_path,
298 fs.path.sep,
299 file.sub_path,
300 @errorName(err),
287 const prev_status = Io.Dir.updateFile(.cwd(), io, source_path, cache_dir.adaptToNewApi(), file.sub_path, .{}) catch |err| {
288 return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {t}", .{
289 source_path, b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
301290 });
302291 };
303292 // At this point we already will mark the step as a cache miss.
......@@ -331,10 +320,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
331320 switch (entry.kind) {
332321 .directory => try cache_dir.makePath(dest_path),
333322 .file => {
334 const prev_status = fs.Dir.updateFile(
335 src_entry_path.root_dir.handle,
323 const prev_status = Io.Dir.updateFile(
324 src_entry_path.root_dir.handle.adaptToNewApi(),
325 io,
336326 src_entry_path.sub_path,
337 cache_dir,
327 cache_dir.adaptToNewApi(),
338328 dest_path,
339329 .{},
340330 ) catch |err| {
lib/std/Build/WebServer.zig+40-27
......@@ -3,14 +3,15 @@ thread_pool: *std.Thread.Pool,
33graph: *const Build.Graph,
44all_steps: []const *Build.Step,
55listen_address: net.IpAddress,
6ttyconf: std.Io.tty.Config,
6ttyconf: Io.tty.Config,
77root_prog_node: std.Progress.Node,
88watch: bool,
99
1010tcp_server: ?net.Server,
1111serve_thread: ?std.Thread,
1212
13base_timestamp: i128,
13/// Uses `Io.Clock.awake`.
14base_timestamp: i96,
1415/// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`.
1516step_names_trailing: []u8,
1617
......@@ -53,15 +54,17 @@ pub const Options = struct {
5354 thread_pool: *std.Thread.Pool,
5455 graph: *const std.Build.Graph,
5556 all_steps: []const *Build.Step,
56 ttyconf: std.Io.tty.Config,
57 ttyconf: Io.tty.Config,
5758 root_prog_node: std.Progress.Node,
5859 watch: bool,
5960 listen_address: net.IpAddress,
61 base_timestamp: Io.Timestamp,
6062};
6163pub fn init(opts: Options) WebServer {
62 // The upcoming `std.Io` interface should allow us to use `Io.async` and `Io.concurrent`
64 // The upcoming `Io` interface should allow us to use `Io.async` and `Io.concurrent`
6365 // instead of threads, so that the web server can function in single-threaded builds.
6466 comptime assert(!builtin.single_threaded);
67 assert(opts.base_timestamp.clock == .awake);
6568
6669 const all_steps = opts.all_steps;
6770
......@@ -106,7 +109,7 @@ pub fn init(opts: Options) WebServer {
106109 .tcp_server = null,
107110 .serve_thread = null,
108111
109 .base_timestamp = std.time.nanoTimestamp(),
112 .base_timestamp = opts.base_timestamp.nanoseconds,
110113 .step_names_trailing = step_names_trailing,
111114
112115 .step_status_bits = step_status_bits,
......@@ -147,32 +150,34 @@ pub fn deinit(ws: *WebServer) void {
147150pub fn start(ws: *WebServer) error{AlreadyReported}!void {
148151 assert(ws.tcp_server == null);
149152 assert(ws.serve_thread == null);
153 const io = ws.graph.io;
150154
151 ws.tcp_server = ws.listen_address.listen(.{ .reuse_address = true }) catch |err| {
155 ws.tcp_server = ws.listen_address.listen(io, .{ .reuse_address = true }) catch |err| {
152156 log.err("failed to listen to port {d}: {s}", .{ ws.listen_address.getPort(), @errorName(err) });
153157 return error.AlreadyReported;
154158 };
155159 ws.serve_thread = std.Thread.spawn(.{}, serve, .{ws}) catch |err| {
156160 log.err("unable to spawn web server thread: {s}", .{@errorName(err)});
157 ws.tcp_server.?.deinit();
161 ws.tcp_server.?.deinit(io);
158162 ws.tcp_server = null;
159163 return error.AlreadyReported;
160164 };
161165
162 log.info("web interface listening at http://{f}/", .{ws.tcp_server.?.listen_address});
166 log.info("web interface listening at http://{f}/", .{ws.tcp_server.?.socket.address});
163167 if (ws.listen_address.getPort() == 0) {
164 log.info("hint: pass '--webui={f}' to use the same port next time", .{ws.tcp_server.?.listen_address});
168 log.info("hint: pass '--webui={f}' to use the same port next time", .{ws.tcp_server.?.socket.address});
165169 }
166170}
167171fn serve(ws: *WebServer) void {
172 const io = ws.graph.io;
168173 while (true) {
169 const connection = ws.tcp_server.?.accept() catch |err| {
174 var stream = ws.tcp_server.?.accept(io) catch |err| {
170175 log.err("failed to accept connection: {s}", .{@errorName(err)});
171176 return;
172177 };
173 _ = std.Thread.spawn(.{}, accept, .{ ws, connection }) catch |err| {
178 _ = std.Thread.spawn(.{}, accept, .{ ws, stream }) catch |err| {
174179 log.err("unable to spawn connection thread: {s}", .{@errorName(err)});
175 connection.stream.close();
180 stream.close(io);
176181 continue;
177182 };
178183 }
......@@ -227,6 +232,7 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
227232
228233 ws.fuzz = Fuzz.init(
229234 ws.gpa,
235 ws.graph.io,
230236 ws.thread_pool,
231237 ws.all_steps,
232238 ws.root_prog_node,
......@@ -241,17 +247,25 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
241247}
242248
243249pub fn now(s: *const WebServer) i64 {
244 return @intCast(std.time.nanoTimestamp() - s.base_timestamp);
250 const io = s.graph.io;
251 const base: Io.Timestamp = .{ .nanoseconds = s.base_timestamp, .clock = .awake };
252 const ts = Io.Timestamp.now(io, base.clock) catch base;
253 return @intCast(base.durationTo(ts).toNanoseconds());
245254}
246255
247fn accept(ws: *WebServer, connection: net.Server.Connection) void {
248 defer connection.stream.close();
249
256fn accept(ws: *WebServer, stream: net.Stream) void {
257 const io = ws.graph.io;
258 defer {
259 // `net.Stream.close` wants to helpfully overwrite `stream` with
260 // `undefined`, but it cannot do so since it is an immutable parameter.
261 var copy = stream;
262 copy.close(io);
263 }
250264 var send_buffer: [4096]u8 = undefined;
251265 var recv_buffer: [4096]u8 = undefined;
252 var connection_reader = connection.stream.reader(&recv_buffer);
253 var connection_writer = connection.stream.writer(&send_buffer);
254 var server: http.Server = .init(connection_reader.interface(), &connection_writer.interface);
266 var connection_reader = stream.reader(io, &recv_buffer);
267 var connection_writer = stream.writer(io, &send_buffer);
268 var server: http.Server = .init(&connection_reader.interface, &connection_writer.interface);
255269
256270 while (true) {
257271 var request = server.receiveHead() catch |err| switch (err) {
......@@ -466,12 +480,9 @@ pub fn serveFile(
466480 },
467481 });
468482}
469pub fn serveTarFile(
470 ws: *WebServer,
471 request: *http.Server.Request,
472 paths: []const Cache.Path,
473) !void {
483pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void {
474484 const gpa = ws.gpa;
485 const io = ws.graph.io;
475486
476487 var send_buffer: [0x4000]u8 = undefined;
477488 var response = try request.respondStreaming(&send_buffer, .{
......@@ -496,7 +507,7 @@ pub fn serveTarFile(
496507 defer file.close();
497508 const stat = try file.stat();
498509 var read_buffer: [1024]u8 = undefined;
499 var file_reader: std.fs.File.Reader = .initSize(file, &read_buffer, stat.size);
510 var file_reader: Io.File.Reader = .initSize(file.adaptToNewApi(), io, &read_buffer, stat.size);
500511
501512 // TODO: this logic is completely bogus -- obviously so, because `path.root_dir.path` can
502513 // be cwd-relative. This is also related to why linkification doesn't work in the fuzzer UI:
......@@ -566,7 +577,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
566577 child.stderr_behavior = .Pipe;
567578 try child.spawn();
568579
569 var poller = std.Io.poll(gpa, enum { stdout, stderr }, .{
580 var poller = Io.poll(gpa, enum { stdout, stderr }, .{
570581 .stdout = child.stdout.?,
571582 .stderr = child.stderr.?,
572583 });
......@@ -842,7 +853,10 @@ const cache_control_header: http.Header = .{
842853};
843854
844855const builtin = @import("builtin");
856
845857const std = @import("std");
858const Io = std.Io;
859const net = std.Io.net;
846860const assert = std.debug.assert;
847861const mem = std.mem;
848862const log = std.log.scoped(.web_server);
......@@ -852,6 +866,5 @@ const Cache = Build.Cache;
852866const Fuzz = Build.Fuzz;
853867const abi = Build.abi;
854868const http = std.http;
855const net = std.Io.net;
856869
857870const WebServer = @This();
lib/std/Io.zig+12
......@@ -654,6 +654,10 @@ pub const VTable = struct {
654654 conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void,
655655 conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void,
656656
657 dirMake: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.MakeError!void,
658 dirStat: *const fn (?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat,
659 dirStatPath: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8) Dir.StatError!File.Stat,
660 fileStat: *const fn (?*anyopaque, file: File) File.StatError!File.Stat,
657661 createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File,
658662 fileOpen: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File,
659663 fileClose: *const fn (?*anyopaque, File) void,
......@@ -804,6 +808,10 @@ pub const Timestamp = struct {
804808 assert(lhs.clock == rhs.clock);
805809 return std.math.compare(lhs.nanoseconds, op, rhs.nanoseconds);
806810 }
811
812 pub fn toSeconds(t: Timestamp) i64 {
813 return @intCast(@divTrunc(t.nanoseconds, std.time.ns_per_s));
814 }
807815};
808816
809817pub const Duration = struct {
......@@ -831,6 +839,10 @@ pub const Duration = struct {
831839 return @intCast(@divTrunc(d.nanoseconds, std.time.ns_per_s));
832840 }
833841
842 pub fn toNanoseconds(d: Duration) i96 {
843 return d.nanoseconds;
844 }
845
834846 pub fn sleep(duration: Duration, io: Io) SleepError!void {
835847 return io.vtable.sleep(io.userdata, .{ .duration = .{ .duration = duration, .clock = .awake } });
836848 }
lib/std/Io/Dir.zig+160-5
......@@ -6,6 +6,9 @@ const File = Io.File;
66
77handle: Handle,
88
9pub const Mode = Io.File.Mode;
10pub const default_mode: Mode = 0o755;
11
912pub fn cwd() Dir {
1013 return .{ .handle = std.fs.cwd().fd };
1114}
......@@ -47,8 +50,9 @@ pub const UpdateFileError = File.OpenError;
4750
4851/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If
4952/// they are equal, does nothing. Otherwise, atomically copies `source_path` to
50/// `dest_path`. The destination file gains the mtime, atime, and mode of the
51/// source file so that the next call to `updateFile` will not need a copy.
53/// `dest_path`, creating the parent directory hierarchy as needed. The
54/// destination file gains the mtime, atime, and mode of the source file so
55/// that the next call to `updateFile` will not need a copy.
5256///
5357/// Returns the previous status of the file before updating.
5458///
......@@ -65,7 +69,7 @@ pub fn updateFile(
6569 options: std.fs.Dir.CopyFileOptions,
6670) !PrevStatus {
6771 var src_file = try source_dir.openFile(io, source_path, .{});
68 defer src_file.close();
72 defer src_file.close(io);
6973
7074 const src_stat = try src_file.stat(io);
7175 const actual_mode = options.override_mode orelse src_stat.mode;
......@@ -93,13 +97,13 @@ pub fn updateFile(
9397 }
9498
9599 var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available.
96 var atomic_file = try dest_dir.atomicFile(io, dest_path, .{
100 var atomic_file = try std.fs.Dir.atomicFile(.adaptFromNewApi(dest_dir), dest_path, .{
97101 .mode = actual_mode,
98102 .write_buffer = &buffer,
99103 });
100104 defer atomic_file.deinit();
101105
102 var src_reader: File.Reader = .initSize(io, src_file, &.{}, src_stat.size);
106 var src_reader: File.Reader = .initSize(src_file, io, &.{}, src_stat.size);
103107 const dest_writer = &atomic_file.file_writer.interface;
104108
105109 _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) {
......@@ -111,3 +115,154 @@ pub fn updateFile(
111115 try atomic_file.renameIntoPlace();
112116 return .stale;
113117}
118
119pub const ReadFileError = File.OpenError || File.Reader.Error;
120
121/// Read all of file contents using a preallocated buffer.
122///
123/// The returned slice has the same pointer as `buffer`. If the length matches `buffer.len`
124/// the situation is ambiguous. It could either mean that the entire file was read, and
125/// it exactly fits the buffer, or it could mean the buffer was not big enough for the
126/// entire file.
127///
128/// * On Windows, `file_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
129/// * On WASI, `file_path` should be encoded as valid UTF-8.
130/// * On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
131pub fn readFile(dir: Dir, io: Io, file_path: []const u8, buffer: []u8) ReadFileError![]u8 {
132 var file = try dir.openFile(io, file_path, .{});
133 defer file.close(io);
134
135 var reader = file.reader(io, &.{});
136 const n = reader.interface.readSliceShort(buffer) catch |err| switch (err) {
137 error.ReadFailed => return reader.err.?,
138 };
139
140 return buffer[0..n];
141}
142
143pub const MakeError = error{
144 /// In WASI, this error may occur when the file descriptor does
145 /// not hold the required rights to create a new directory relative to it.
146 AccessDenied,
147 PermissionDenied,
148 DiskQuota,
149 PathAlreadyExists,
150 SymLinkLoop,
151 LinkQuotaExceeded,
152 NameTooLong,
153 FileNotFound,
154 SystemResources,
155 NoSpaceLeft,
156 NotDir,
157 ReadOnlyFileSystem,
158 /// WASI-only; file paths must be valid UTF-8.
159 InvalidUtf8,
160 /// Windows-only; file paths provided by the user must be valid WTF-8.
161 /// https://simonsapin.github.io/wtf-8/
162 InvalidWtf8,
163 BadPathName,
164 NoDevice,
165 /// On Windows, `\\server` or `\\server\share` was not found.
166 NetworkNotFound,
167} || Io.Cancelable || Io.UnexpectedError;
168
169/// Creates a single directory with a relative or absolute path.
170///
171/// * On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
172/// * On WASI, `sub_path` should be encoded as valid UTF-8.
173/// * On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
174///
175/// Related:
176/// * `makePath`
177/// * `makeDirAbsolute`
178pub fn makeDir(dir: Dir, io: Io, sub_path: []const u8) MakeError!void {
179 return io.vtable.dirMake(io.userdata, dir, sub_path, default_mode);
180}
181
182pub const MakePathError = MakeError || StatPathError;
183
184/// Calls makeDir iteratively to make an entire path, creating any parent
185/// directories that do not exist.
186///
187/// Returns success if the path already exists and is a directory.
188///
189/// This function is not atomic, and if it returns an error, the file system
190/// may have been modified regardless.
191///
192/// Fails on an empty path with `error.BadPathName` as that is not a path that
193/// can be created.
194///
195/// On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
196/// On WASI, `sub_path` should be encoded as valid UTF-8.
197/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
198///
199/// Paths containing `..` components are handled differently depending on the platform:
200/// - On Windows, `..` are resolved before the path is passed to NtCreateFile, meaning
201/// a `sub_path` like "first/../second" will resolve to "second" and only a
202/// `./second` directory will be created.
203/// - On other platforms, `..` are not resolved before the path is passed to `mkdirat`,
204/// meaning a `sub_path` like "first/../second" will create both a `./first`
205/// and a `./second` directory.
206pub fn makePath(dir: Dir, io: Io, sub_path: []const u8) MakePathError!void {
207 _ = try makePathStatus(dir, io, sub_path);
208}
209
210pub const MakePathStatus = enum { existed, created };
211
212/// Same as `makePath` except returns whether the path already existed or was
213/// successfully created.
214pub fn makePathStatus(dir: Dir, io: Io, sub_path: []const u8) MakePathError!MakePathStatus {
215 var it = try std.fs.path.componentIterator(sub_path);
216 var status: MakePathStatus = .existed;
217 var component = it.last() orelse return error.BadPathName;
218 while (true) {
219 if (makeDir(dir, io, component.path)) |_| {
220 status = .created;
221 } else |err| switch (err) {
222 error.PathAlreadyExists => {
223 // stat the file and return an error if it's not a directory
224 // this is important because otherwise a dangling symlink
225 // could cause an infinite loop
226 check_dir: {
227 // workaround for windows, see https://github.com/ziglang/zig/issues/16738
228 const fstat = statPath(dir, io, component.path) catch |stat_err| switch (stat_err) {
229 error.IsDir => break :check_dir,
230 else => |e| return e,
231 };
232 if (fstat.kind != .directory) return error.NotDir;
233 }
234 },
235 error.FileNotFound => |e| {
236 component = it.previous() orelse return e;
237 continue;
238 },
239 else => |e| return e,
240 }
241 component = it.next() orelse return status;
242 }
243}
244
245pub const Stat = File.Stat;
246pub const StatError = File.StatError;
247
248pub fn stat(dir: Dir, io: Io) StatError!Stat {
249 return io.vtable.dirStat(io.userdata, dir);
250}
251
252pub const StatPathError = File.OpenError || File.StatError;
253
254/// Returns metadata for a file inside the directory.
255///
256/// On Windows, this requires three syscalls. On other operating systems, it
257/// only takes one.
258///
259/// Symlinks are followed.
260///
261/// `sub_path` may be absolute, in which case `self` is ignored.
262///
263/// * On Windows, `sub_path` should be encoded as [WTF-8](https://simonsapin.github.io/wtf-8/).
264/// * On WASI, `sub_path` should be encoded as valid UTF-8.
265/// * On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
266pub fn statPath(dir: Dir, io: Io, sub_path: []const u8) StatPathError!File.Stat {
267 return io.vtable.dirStatPath(io.userdata, dir, sub_path);
268}
lib/std/Io/File.zig+5-1
......@@ -446,7 +446,11 @@ pub const Reader = struct {
446446
447447 fn stream(io_reader: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
448448 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));
449 switch (r.mode) {
449 return streamMode(r, w, limit, r.mode);
450 }
451
452 pub fn streamMode(r: *Reader, w: *Io.Writer, limit: Io.Limit, mode: Reader.Mode) Io.Reader.StreamError!usize {
453 switch (mode) {
450454 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
451455 error.Unimplemented => {
452456 r.mode = r.mode.toReading();
lib/std/Io/Threaded.zig+69-4
......@@ -63,7 +63,17 @@ const Closure = struct {
6363
6464pub const InitError = std.Thread.CpuCountError || Allocator.Error;
6565
66pub fn init(gpa: Allocator) Pool {
66/// Related:
67/// * `init_single_threaded`
68pub fn init(
69 /// Must be threadsafe. Only used for the following functions:
70 /// * `Io.VTable.async`
71 /// * `Io.VTable.concurrent`
72 /// * `Io.VTable.groupAsync`
73 /// If these functions are avoided, then `Allocator.failing` may be passed
74 /// here.
75 gpa: Allocator,
76) Pool {
6777 var pool: Pool = .{
6878 .allocator = gpa,
6979 .threads = .empty,
......@@ -77,6 +87,20 @@ pub fn init(gpa: Allocator) Pool {
7787 return pool;
7888}
7989
90/// Statically initialize such that any call to the following functions will
91/// fail with `error.OutOfMemory`:
92/// * `Io.VTable.async`
93/// * `Io.VTable.concurrent`
94/// * `Io.VTable.groupAsync`
95/// When initialized this way, `deinit` is safe, but unnecessary to call.
96pub const init_single_threaded: Pool = .{
97 .allocator = .failing,
98 .threads = .empty,
99 .stack_size = std.Thread.SpawnConfig.default_stack_size,
100 .cpu_count = 1,
101 .concurrent_count = 0,
102};
103
80104pub fn deinit(pool: *Pool) void {
81105 const gpa = pool.allocator;
82106 pool.join();
......@@ -136,6 +160,10 @@ pub fn io(pool: *Pool) Io {
136160 .conditionWait = conditionWait,
137161 .conditionWake = conditionWake,
138162
163 .dirMake = dirMake,
164 .dirStat = dirStat,
165 .dirStatPath = dirStatPath,
166 .fileStat = fileStat,
139167 .createFile = createFile,
140168 .fileOpen = fileOpen,
141169 .fileClose = fileClose,
......@@ -520,10 +548,11 @@ fn groupAsync(
520548
521549fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
522550 const pool: *Pool = @ptrCast(@alignCast(userdata));
523 _ = pool;
551 const gpa = pool.allocator;
524552
525553 if (builtin.single_threaded) return;
526554
555 // TODO these primitives are too high level, need to check cancel on EINTR
527556 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
528557 const reset_event: *ResetEvent = @ptrCast(&group.context);
529558 std.Thread.WaitGroup.waitStateless(group_state, reset_event);
......@@ -531,8 +560,9 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
531560 var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token));
532561 while (true) {
533562 const gc: *GroupClosure = @fieldParentPtr("node", node);
534 gc.closure.requestCancel();
535 node = node.next orelse break;
563 const node_next = node.next;
564 gc.free(gpa);
565 node = node_next orelse break;
536566 }
537567}
538568
......@@ -724,6 +754,41 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.
724754 }
725755}
726756
757fn dirMake(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void {
758 const pool: *Pool = @ptrCast(@alignCast(userdata));
759 try pool.checkCancel();
760
761 _ = dir;
762 _ = sub_path;
763 _ = mode;
764 @panic("TODO");
765}
766
767fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat {
768 const pool: *Pool = @ptrCast(@alignCast(userdata));
769 try pool.checkCancel();
770
771 _ = dir;
772 @panic("TODO");
773}
774
775fn dirStatPath(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8) Io.Dir.StatError!Io.File.Stat {
776 const pool: *Pool = @ptrCast(@alignCast(userdata));
777 try pool.checkCancel();
778
779 _ = dir;
780 _ = sub_path;
781 @panic("TODO");
782}
783
784fn fileStat(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
785 const pool: *Pool = @ptrCast(@alignCast(userdata));
786 try pool.checkCancel();
787
788 _ = file;
789 @panic("TODO");
790}
791
727792fn createFile(
728793 userdata: ?*anyopaque,
729794 dir: Io.Dir,
lib/std/Io/Writer.zig+6-2
......@@ -2827,6 +2827,8 @@ pub const Allocating = struct {
28272827};
28282828
28292829test "discarding sendFile" {
2830 const io = testing.io;
2831
28302832 var tmp_dir = testing.tmpDir(.{});
28312833 defer tmp_dir.cleanup();
28322834
......@@ -2837,7 +2839,7 @@ test "discarding sendFile" {
28372839 try file_writer.interface.writeByte('h');
28382840 try file_writer.interface.flush();
28392841
2840 var file_reader = file_writer.moveToReader();
2842 var file_reader = file_writer.moveToReader(io);
28412843 try file_reader.seekTo(0);
28422844
28432845 var w_buffer: [256]u8 = undefined;
......@@ -2847,6 +2849,8 @@ test "discarding sendFile" {
28472849}
28482850
28492851test "allocating sendFile" {
2852 const io = testing.io;
2853
28502854 var tmp_dir = testing.tmpDir(.{});
28512855 defer tmp_dir.cleanup();
28522856
......@@ -2857,7 +2861,7 @@ test "allocating sendFile" {
28572861 try file_writer.interface.writeAll("abcd");
28582862 try file_writer.interface.flush();
28592863
2860 var file_reader = file_writer.moveToReader();
2864 var file_reader = file_writer.moveToReader(io);
28612865 try file_reader.seekTo(0);
28622866 try file_reader.interface.fill(2);
28632867
lib/std/Io/net.zig+33
......@@ -57,6 +57,39 @@ pub const IpAddress = union(enum) {
5757
5858 pub const Family = @typeInfo(IpAddress).@"union".tag_type.?;
5959
60 pub const ParseLiteralError = error{ InvalidAddress, InvalidPort };
61
62 /// Parse an IP address which may include a port.
63 ///
64 /// For IPv4, this is written `address:port`.
65 ///
66 /// For IPv6, RFC 3986 defines this as an "IP literal", and the port is
67 /// differentiated from the address by surrounding the address part in
68 /// brackets "[addr]:port". Even if the port is not given, the brackets are
69 /// mandatory.
70 pub fn parseLiteral(text: []const u8) ParseLiteralError!IpAddress {
71 if (text.len == 0) return error.InvalidAddress;
72 if (text[0] == '[') {
73 const addr_end = std.mem.indexOfScalar(u8, text, ']') orelse
74 return error.InvalidAddress;
75 const addr_text = text[1..addr_end];
76 const port: u16 = p: {
77 if (addr_end == text.len - 1) break :p 0;
78 if (text[addr_end + 1] != ':') return error.InvalidAddress;
79 break :p std.fmt.parseInt(u16, text[addr_end + 2 ..], 10) catch return error.InvalidPort;
80 };
81 return parseIp6(addr_text, port) catch error.InvalidAddress;
82 }
83 if (std.mem.indexOfScalar(u8, text, ':')) |i| {
84 const addr = Ip4Address.parse(text[0..i], 0) catch return error.InvalidAddress;
85 return .{ .ip4 = .{
86 .bytes = addr.bytes,
87 .port = std.fmt.parseInt(u16, text[i + 1 ..], 10) catch return error.InvalidPort,
88 } };
89 }
90 return parseIp4(text, 0) catch error.InvalidAddress;
91 }
92
6093 /// Parse the given IP address string into an `IpAddress` value.
6194 ///
6295 /// This is a pure function but it cannot handle IPv6 addresses that have
lib/std/Io/net/HostName.zig+16-3
......@@ -77,7 +77,9 @@ pub const LookupError = error{
7777 InvalidDnsAAAARecord,
7878 InvalidDnsCnameRecord,
7979 NameServerFailure,
80} || Io.Timestamp.Error || IpAddress.BindError || Io.File.OpenError || Io.File.Reader.Error || Io.Cancelable;
80 /// Failed to open or read "/etc/hosts" or "/etc/resolv.conf".
81 DetectingNetworkConfigurationFailed,
82} || Io.Timestamp.Error || IpAddress.BindError || Io.Cancelable;
8183
8284pub const LookupResult = struct {
8385 /// How many `LookupOptions.addresses_buffer` elements are populated.
......@@ -428,14 +430,25 @@ fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResul
428430 error.AccessDenied,
429431 => return .empty,
430432
431 else => |e| return e,
433 error.Canceled => |e| return e,
434
435 else => {
436 // TODO populate optional diagnostic struct
437 return error.DetectingNetworkConfigurationFailed;
438 },
432439 };
433440 defer file.close(io);
434441
435442 var line_buf: [512]u8 = undefined;
436443 var file_reader = file.reader(io, &line_buf);
437444 return lookupHostsReader(host_name, options, &file_reader.interface) catch |err| switch (err) {
438 error.ReadFailed => return file_reader.err.?,
445 error.ReadFailed => switch (file_reader.err.?) {
446 error.Canceled => |e| return e,
447 else => {
448 // TODO populate optional diagnostic struct
449 return error.DetectingNetworkConfigurationFailed;
450 },
451 },
439452 };
440453}
441454
lib/std/Io/net/test.zig+15-16
......@@ -211,11 +211,11 @@ test "listen on a port, send bytes, receive bytes" {
211211 const t = try std.Thread.spawn(.{}, S.clientFn, .{server.socket.address});
212212 defer t.join();
213213
214 var client = try server.accept(io);
215 defer client.stream.close(io);
214 var stream = try server.accept(io);
215 defer stream.close(io);
216216 var buf: [16]u8 = undefined;
217 var stream_reader = client.stream.reader(io, &.{});
218 const n = try stream_reader.interface().readSliceShort(&buf);
217 var stream_reader = stream.reader(io, &.{});
218 const n = try stream_reader.interface.readSliceShort(&buf);
219219
220220 try testing.expectEqual(@as(usize, 12), n);
221221 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
......@@ -267,10 +267,9 @@ fn testServer(server: *net.Server) anyerror!void {
267267
268268 const io = testing.io;
269269
270 var client = try server.accept(io);
271
272 const stream = client.stream.writer(io);
273 try stream.print("hello from server\n", .{});
270 var stream = try server.accept(io);
271 var writer = stream.writer(io, &.{});
272 try writer.interface.print("hello from server\n", .{});
274273}
275274
276275test "listen on a unix socket, send bytes, receive bytes" {
......@@ -310,11 +309,11 @@ test "listen on a unix socket, send bytes, receive bytes" {
310309 const t = try std.Thread.spawn(.{}, S.clientFn, .{socket_path});
311310 defer t.join();
312311
313 var client = try server.accept(io);
314 defer client.stream.close(io);
312 var stream = try server.accept(io);
313 defer stream.close(io);
315314 var buf: [16]u8 = undefined;
316 var stream_reader = client.stream.reader(io, &.{});
317 const n = try stream_reader.interface().readSliceShort(&buf);
315 var stream_reader = stream.reader(io, &.{});
316 const n = try stream_reader.interface.readSliceShort(&buf);
318317
319318 try testing.expectEqual(@as(usize, 12), n);
320319 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
......@@ -366,10 +365,10 @@ test "non-blocking tcp server" {
366365 const socket_file = try net.tcpConnectToAddress(server.socket.address);
367366 defer socket_file.close();
368367
369 var client = try server.accept(io);
370 defer client.stream.close(io);
371 const stream = client.stream.writer(io);
372 try stream.print("hello from server\n", .{});
368 var stream = try server.accept(io);
369 defer stream.close(io);
370 var writer = stream.writer(io, .{});
371 try writer.interface.print("hello from server\n", .{});
373372
374373 var buf: [100]u8 = undefined;
375374 const len = try socket_file.read(&buf);
lib/std/crypto/tls/Client.zig+12-8
......@@ -105,6 +105,14 @@ pub const Options = struct {
105105 /// Verify that the server certificate is authorized by a given ca bundle.
106106 bundle: Certificate.Bundle,
107107 },
108 write_buffer: []u8,
109 read_buffer: []u8,
110 /// Cryptographically secure random bytes. The pointer is not captured; data is only
111 /// read during `init`.
112 entropy: *const [176]u8,
113 /// Current time according to the wall clock / calendar, in seconds.
114 realtime_now_seconds: i64,
115
108116 /// If non-null, ssl secrets are logged to this stream. Creating such a log file allows
109117 /// other programs with access to that file to decrypt all traffic over this connection.
110118 ///
......@@ -120,8 +128,6 @@ pub const Options = struct {
120128 /// application layer itself verifies that the amount of data received equals
121129 /// the amount of data expected, such as HTTP with the Content-Length header.
122130 allow_truncation_attacks: bool = false,
123 write_buffer: []u8,
124 read_buffer: []u8,
125131 /// Populated when `error.TlsAlert` is returned from `init`.
126132 alert: ?*tls.Alert = null,
127133};
......@@ -189,14 +195,12 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
189195 };
190196 const host_len: u16 = @intCast(host.len);
191197
192 var random_buffer: [176]u8 = undefined;
193 crypto.random.bytes(&random_buffer);
194 const client_hello_rand = random_buffer[0..32].*;
198 const client_hello_rand = options.entropy[0..32].*;
195199 var key_seq: u64 = 0;
196200 var server_hello_rand: [32]u8 = undefined;
197 const legacy_session_id = random_buffer[32..64].*;
201 const legacy_session_id = options.entropy[32..64].*;
198202
199 var key_share = KeyShare.init(random_buffer[64..176].*) catch |err| switch (err) {
203 var key_share = KeyShare.init(options.entropy[64..176].*) catch |err| switch (err) {
200204 // Only possible to happen if the seed is all zeroes.
201205 error.IdentityElement => return error.InsufficientEntropy,
202206 };
......@@ -321,7 +325,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
321325 var handshake_cipher: tls.HandshakeCipher = undefined;
322326 var main_cert_pub_key: CertificatePublicKey = undefined;
323327 var tls12_negotiated_group: ?tls.NamedGroup = null;
324 const now_sec = std.time.timestamp();
328 const now_sec = options.realtime_now_seconds;
325329
326330 var cleartext_fragment_start: usize = 0;
327331 var cleartext_fragment_end: usize = 0;
lib/std/debug/SelfInfo/Windows.zig+2-1
......@@ -434,7 +434,7 @@ const Module = struct {
434434 };
435435 errdefer pdb_file.close();
436436
437 const pdb_reader = try arena.create(std.fs.File.Reader);
437 const pdb_reader = try arena.create(Io.File.Reader);
438438 pdb_reader.* = pdb_file.reader(try arena.alloc(u8, 4096));
439439
440440 var pdb = Pdb.init(gpa, pdb_reader) catch |err| switch (err) {
......@@ -544,6 +544,7 @@ fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebug
544544}
545545
546546const std = @import("std");
547const Io = std.Io;
547548const Allocator = std.mem.Allocator;
548549const Dwarf = std.debug.Dwarf;
549550const Pdb = std.debug.Pdb;
lib/std/elf.zig+6-6
......@@ -710,7 +710,7 @@ pub const ProgramHeaderIterator = struct {
710710 const offset = it.phoff + size * it.index;
711711 try it.file_reader.seekTo(offset);
712712
713 return takeProgramHeader(&it.file_reader.interface, it.is_64, it.endian);
713 return try takeProgramHeader(&it.file_reader.interface, it.is_64, it.endian);
714714 }
715715};
716716
......@@ -731,7 +731,7 @@ pub const ProgramHeaderBufferIterator = struct {
731731 const offset = it.phoff + size * it.index;
732732 var reader = Io.Reader.fixed(it.buf[offset..]);
733733
734 return takeProgramHeader(&reader, it.is_64, it.endian);
734 return try takeProgramHeader(&reader, it.is_64, it.endian);
735735 }
736736};
737737
......@@ -771,7 +771,7 @@ pub const SectionHeaderIterator = struct {
771771 const offset = it.shoff + size * it.index;
772772 try it.file_reader.seekTo(offset);
773773
774 return takeSectionHeader(&it.file_reader.interface, it.is_64, it.endian);
774 return try takeSectionHeader(&it.file_reader.interface, it.is_64, it.endian);
775775 }
776776};
777777
......@@ -793,7 +793,7 @@ pub const SectionHeaderBufferIterator = struct {
793793 if (offset > it.buf.len) return error.EndOfStream;
794794 var reader = Io.Reader.fixed(it.buf[@intCast(offset)..]);
795795
796 return takeSectionHeader(&reader, it.is_64, it.endian);
796 return try takeSectionHeader(&reader, it.is_64, it.endian);
797797 }
798798};
799799
......@@ -826,12 +826,12 @@ pub const DynamicSectionIterator = struct {
826826
827827 file_reader: *Io.File.Reader,
828828
829 pub fn next(it: *SectionHeaderIterator) !?Elf64_Dyn {
829 pub fn next(it: *DynamicSectionIterator) !?Elf64_Dyn {
830830 if (it.offset >= it.end_offset) return null;
831831 const size: u64 = if (it.is_64) @sizeOf(Elf64_Dyn) else @sizeOf(Elf32_Dyn);
832832 defer it.offset += size;
833833 try it.file_reader.seekTo(it.offset);
834 return takeDynamicSection(&it.file_reader.interface, it.is_64, it.endian);
834 return try takeDynamicSection(&it.file_reader.interface, it.is_64, it.endian);
835835 }
836836};
837837
lib/std/fs/Dir.zig+56-94
......@@ -1,6 +1,11 @@
1//! Deprecated in favor of `Io.Dir`.
12const Dir = @This();
3
24const builtin = @import("builtin");
5const native_os = builtin.os.tag;
6
37const std = @import("../std.zig");
8const Io = std.Io;
49const File = std.fs.File;
510const AtomicFile = std.fs.AtomicFile;
611const base64_encoder = fs.base64_encoder;
......@@ -12,7 +17,6 @@ const Allocator = std.mem.Allocator;
1217const assert = std.debug.assert;
1318const linux = std.os.linux;
1419const windows = std.os.windows;
15const native_os = builtin.os.tag;
1620const have_flock = @TypeOf(posix.system.flock) != void;
1721
1822fd: Handle,
......@@ -1189,84 +1193,41 @@ pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags)
11891193 return file;
11901194}
11911195
1192pub const MakeError = posix.MakeDirError;
1196/// Deprecated in favor of `Io.Dir.MakeError`.
1197pub const MakeError = Io.Dir.MakeError;
11931198
1194/// Creates a single directory with a relative or absolute path.
1195/// To create multiple directories to make an entire path, see `makePath`.
1196/// To operate on only absolute paths, see `makeDirAbsolute`.
1197/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
1198/// On WASI, `sub_path` should be encoded as valid UTF-8.
1199/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1199/// Deprecated in favor of `Io.Dir.makeDir`.
12001200pub fn makeDir(self: Dir, sub_path: []const u8) MakeError!void {
1201 try posix.mkdirat(self.fd, sub_path, default_mode);
1201 var threaded: Io.Threaded = .init_single_threaded;
1202 const io = threaded.io();
1203 return Io.Dir.makeDir(.{ .handle = self.fd }, io, sub_path);
12021204}
12031205
1204/// Same as `makeDir`, but `sub_path` is null-terminated.
1205/// To create multiple directories to make an entire path, see `makePath`.
1206/// To operate on only absolute paths, see `makeDirAbsoluteZ`.
1206/// Deprecated in favor of `Io.Dir.makeDir`.
12071207pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) MakeError!void {
12081208 try posix.mkdiratZ(self.fd, sub_path, default_mode);
12091209}
12101210
1211/// Creates a single directory with a relative or absolute null-terminated WTF-16 LE-encoded path.
1212/// To create multiple directories to make an entire path, see `makePath`.
1213/// To operate on only absolute paths, see `makeDirAbsoluteW`.
1211/// Deprecated in favor of `Io.Dir.makeDir`.
12141212pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {
12151213 try posix.mkdiratW(self.fd, mem.span(sub_path), default_mode);
12161214}
12171215
1218/// Calls makeDir iteratively to make an entire path
1219/// (i.e. creating any parent directories that do not exist).
1220/// Returns success if the path already exists and is a directory.
1221/// This function is not atomic, and if it returns an error, the file system may
1222/// have been modified regardless.
1223/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
1224/// On WASI, `sub_path` should be encoded as valid UTF-8.
1225/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
1226/// Fails on an empty path with `error.BadPathName` as that is not a path that can be created.
1227///
1228/// Paths containing `..` components are handled differently depending on the platform:
1229/// - On Windows, `..` are resolved before the path is passed to NtCreateFile, meaning
1230/// a `sub_path` like "first/../second" will resolve to "second" and only a
1231/// `./second` directory will be created.
1232/// - On other platforms, `..` are not resolved before the path is passed to `mkdirat`,
1233/// meaning a `sub_path` like "first/../second" will create both a `./first`
1234/// and a `./second` directory.
1235pub fn makePath(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!void {
1216/// Deprecated in favor of `Io.Dir.makePath`.
1217pub fn makePath(self: Dir, sub_path: []const u8) MakePathError!void {
12361218 _ = try self.makePathStatus(sub_path);
12371219}
12381220
1239pub const MakePathStatus = enum { existed, created };
1240/// Same as `makePath` except returns whether the path already existed or was successfully created.
1241pub fn makePathStatus(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!MakePathStatus {
1242 var it = try fs.path.componentIterator(sub_path);
1243 var status: MakePathStatus = .existed;
1244 var component = it.last() orelse return error.BadPathName;
1245 while (true) {
1246 if (self.makeDir(component.path)) |_| {
1247 status = .created;
1248 } else |err| switch (err) {
1249 error.PathAlreadyExists => {
1250 // stat the file and return an error if it's not a directory
1251 // this is important because otherwise a dangling symlink
1252 // could cause an infinite loop
1253 check_dir: {
1254 // workaround for windows, see https://github.com/ziglang/zig/issues/16738
1255 const fstat = self.statFile(component.path) catch |stat_err| switch (stat_err) {
1256 error.IsDir => break :check_dir,
1257 else => |e| return e,
1258 };
1259 if (fstat.kind != .directory) return error.NotDir;
1260 }
1261 },
1262 error.FileNotFound => |e| {
1263 component = it.previous() orelse return e;
1264 continue;
1265 },
1266 else => |e| return e,
1267 }
1268 component = it.next() orelse return status;
1269 }
1221/// Deprecated in favor of `Io.Dir.MakePathStatus`.
1222pub const MakePathStatus = Io.Dir.MakePathStatus;
1223/// Deprecated in favor of `Io.Dir.MakePathError`.
1224pub const MakePathError = Io.Dir.MakePathError;
1225
1226/// Deprecated in favor of `Io.Dir.makePathStatus`.
1227pub fn makePathStatus(self: Dir, sub_path: []const u8) MakePathError!MakePathStatus {
1228 var threaded: Io.Threaded = .init_single_threaded;
1229 const io = threaded.io();
1230 return Io.Dir.makePathStatus(.{ .handle = self.fd }, io, sub_path);
12701231}
12711232
12721233/// Windows only. Calls makeOpenDirAccessMaskW iteratively to make an entire path
......@@ -2052,20 +2013,11 @@ pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
20522013 return windows.ReadLink(self.fd, sub_path_w, buffer);
20532014}
20542015
2055/// Read all of file contents using a preallocated buffer.
2056/// The returned slice has the same pointer as `buffer`. If the length matches `buffer.len`
2057/// the situation is ambiguous. It could either mean that the entire file was read, and
2058/// it exactly fits the buffer, or it could mean the buffer was not big enough for the
2059/// entire file.
2060/// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
2061/// On WASI, `file_path` should be encoded as valid UTF-8.
2062/// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding.
2016/// Deprecated in favor of `Io.Dir.readFile`.
20632017pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 {
2064 var file = try self.openFile(file_path, .{});
2065 defer file.close();
2066
2067 const end_index = try file.readAll(buffer);
2068 return buffer[0..end_index];
2018 var threaded: Io.Threaded = .init_single_threaded;
2019 const io = threaded.io();
2020 return Io.Dir.readFile(.{ .handle = self.fd }, io, file_path, buffer);
20692021}
20702022
20712023pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{
......@@ -2091,7 +2043,7 @@ pub fn readFileAlloc(
20912043 /// Used to allocate the result.
20922044 gpa: Allocator,
20932045 /// If reached or exceeded, `error.StreamTooLong` is returned instead.
2094 limit: std.Io.Limit,
2046 limit: Io.Limit,
20952047) ReadFileAllocError![]u8 {
20962048 return readFileAllocOptions(dir, sub_path, gpa, limit, .of(u8), null);
20972049}
......@@ -2101,6 +2053,8 @@ pub fn readFileAlloc(
21012053///
21022054/// If the file size is already known, a better alternative is to initialize a
21032055/// `File.Reader`.
2056///
2057/// TODO move this function to Io.Dir
21042058pub fn readFileAllocOptions(
21052059 dir: Dir,
21062060 /// On Windows, should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
......@@ -2110,13 +2064,16 @@ pub fn readFileAllocOptions(
21102064 /// Used to allocate the result.
21112065 gpa: Allocator,
21122066 /// If reached or exceeded, `error.StreamTooLong` is returned instead.
2113 limit: std.Io.Limit,
2067 limit: Io.Limit,
21142068 comptime alignment: std.mem.Alignment,
21152069 comptime sentinel: ?u8,
21162070) ReadFileAllocError!(if (sentinel) |s| [:s]align(alignment.toByteUnits()) u8 else []align(alignment.toByteUnits()) u8) {
2071 var threaded: Io.Threaded = .init_single_threaded;
2072 const io = threaded.io();
2073
21172074 var file = try dir.openFile(sub_path, .{});
21182075 defer file.close();
2119 var file_reader = file.reader(&.{});
2076 var file_reader = file.reader(io, &.{});
21202077 return file_reader.interface.allocRemainingAlignedSentinel(gpa, limit, alignment, sentinel) catch |err| switch (err) {
21212078 error.ReadFailed => return file_reader.err.?,
21222079 error.OutOfMemory, error.StreamTooLong => |e| return e,
......@@ -2647,6 +2604,8 @@ pub const CopyFileError = File.OpenError || File.StatError ||
26472604/// [WTF-8](https://wtf-8.codeberg.page/). On WASI, both paths should be
26482605/// encoded as valid UTF-8. On other platforms, both paths are an opaque
26492606/// sequence of bytes with no particular encoding.
2607///
2608/// TODO move this function to Io.Dir
26502609pub fn copyFile(
26512610 source_dir: Dir,
26522611 source_path: []const u8,
......@@ -2654,11 +2613,15 @@ pub fn copyFile(
26542613 dest_path: []const u8,
26552614 options: CopyFileOptions,
26562615) CopyFileError!void {
2657 var file_reader: File.Reader = .init(try source_dir.openFile(source_path, .{}), &.{});
2658 defer file_reader.file.close();
2616 var threaded: Io.Threaded = .init_single_threaded;
2617 const io = threaded.io();
2618
2619 const file = try source_dir.openFile(source_path, .{});
2620 var file_reader: File.Reader = .init(.{ .handle = file.handle }, io, &.{});
2621 defer file_reader.file.close(io);
26592622
26602623 const mode = options.override_mode orelse blk: {
2661 const st = try file_reader.file.stat();
2624 const st = try file_reader.file.stat(io);
26622625 file_reader.size = st.size;
26632626 break :blk st.mode;
26642627 };
......@@ -2708,6 +2671,7 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions)
27082671pub const Stat = File.Stat;
27092672pub const StatError = File.StatError;
27102673
2674/// Deprecated in favor of `Io.Dir.stat`.
27112675pub fn stat(self: Dir) StatError!Stat {
27122676 const file: File = .{ .handle = self.fd };
27132677 return file.stat();
......@@ -2715,17 +2679,7 @@ pub fn stat(self: Dir) StatError!Stat {
27152679
27162680pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError;
27172681
2718/// Returns metadata for a file inside the directory.
2719///
2720/// On Windows, this requires three syscalls. On other operating systems, it
2721/// only takes one.
2722///
2723/// Symlinks are followed.
2724///
2725/// `sub_path` may be absolute, in which case `self` is ignored.
2726/// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
2727/// On WASI, `sub_path` should be encoded as valid UTF-8.
2728/// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding.
2682/// Deprecated in favor of `Io.Dir.statPath`.
27292683pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
27302684 if (native_os == .windows) {
27312685 var file = try self.openFile(sub_path, .{});
......@@ -2799,3 +2753,11 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v
27992753 const file: File = .{ .handle = self.fd };
28002754 try file.setPermissions(permissions);
28012755}
2756
2757pub fn adaptToNewApi(dir: Dir) Io.Dir {
2758 return .{ .handle = dir.fd };
2759}
2760
2761pub fn adaptFromNewApi(dir: Io.Dir) Dir {
2762 return .{ .fd = dir.handle };
2763}
lib/std/fs/File.zig+16-6
......@@ -858,10 +858,12 @@ pub const Writer = struct {
858858 };
859859 }
860860
861 pub fn moveToReader(w: *Writer) Reader {
861 /// TODO when this logic moves from fs.File to Io.File the io parameter should be deleted
862 pub fn moveToReader(w: *Writer, io: std.Io) Reader {
862863 defer w.* = undefined;
863864 return .{
864 .file = w.file,
865 .io = io,
866 .file = .{ .handle = w.file.handle },
865867 .mode = w.mode,
866868 .pos = w.pos,
867869 .interface = Reader.initInterface(w.interface.buffer),
......@@ -1350,15 +1352,15 @@ pub const Writer = struct {
13501352///
13511353/// Positional is more threadsafe, since the global seek position is not
13521354/// affected.
1353pub fn reader(file: File, buffer: []u8) Reader {
1354 return .init(file, buffer);
1355pub fn reader(file: File, io: std.Io, buffer: []u8) Reader {
1356 return .init(.{ .handle = file.handle }, io, buffer);
13551357}
13561358
13571359/// Positional is more threadsafe, since the global seek position is not
13581360/// affected, but when such syscalls are not available, preemptively
13591361/// initializing in streaming mode skips a failed syscall.
1360pub fn readerStreaming(file: File, buffer: []u8) Reader {
1361 return .initStreaming(file, buffer);
1362pub fn readerStreaming(file: File, io: std.Io, buffer: []u8) Reader {
1363 return .initStreaming(.{ .handle = file.handle }, io, buffer);
13621364}
13631365
13641366/// Defaults to positional reading; falls back to streaming.
......@@ -1538,3 +1540,11 @@ pub fn downgradeLock(file: File) LockError!void {
15381540 };
15391541 }
15401542}
1543
1544pub fn adaptToNewApi(file: File) std.Io.File {
1545 return .{ .handle = file.handle };
1546}
1547
1548pub fn adaptFromNewApi(file: std.Io.File) File {
1549 return .{ .handle = file.handle };
1550}
lib/std/fs/test.zig+66-105
......@@ -1,10 +1,12 @@
1const std = @import("../std.zig");
21const builtin = @import("builtin");
2const native_os = builtin.os.tag;
3
4const std = @import("../std.zig");
5const Io = std.Io;
36const testing = std.testing;
47const fs = std.fs;
58const mem = std.mem;
69const wasi = std.os.wasi;
7const native_os = builtin.os.tag;
810const windows = std.os.windows;
911const posix = std.posix;
1012
......@@ -73,6 +75,7 @@ const PathType = enum {
7375};
7476
7577const TestContext = struct {
78 io: Io,
7679 path_type: PathType,
7780 path_sep: u8,
7881 arena: ArenaAllocator,
......@@ -83,6 +86,7 @@ const TestContext = struct {
8386 pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {
8487 const tmp = tmpDir(.{ .iterate = true });
8588 return .{
89 .io = testing.io,
8690 .path_type = path_type,
8791 .path_sep = path_sep,
8892 .arena = ArenaAllocator.init(allocator),
......@@ -1319,6 +1323,8 @@ test "max file name component lengths" {
13191323}
13201324
13211325test "writev, readv" {
1326 const io = testing.io;
1327
13221328 var tmp = tmpDir(.{});
13231329 defer tmp.cleanup();
13241330
......@@ -1327,78 +1333,55 @@ test "writev, readv" {
13271333
13281334 var buf1: [line1.len]u8 = undefined;
13291335 var buf2: [line2.len]u8 = undefined;
1330 var write_vecs = [_]posix.iovec_const{
1331 .{
1332 .base = line1,
1333 .len = line1.len,
1334 },
1335 .{
1336 .base = line2,
1337 .len = line2.len,
1338 },
1339 };
1340 var read_vecs = [_]posix.iovec{
1341 .{
1342 .base = &buf2,
1343 .len = buf2.len,
1344 },
1345 .{
1346 .base = &buf1,
1347 .len = buf1.len,
1348 },
1349 };
1336 var write_vecs: [2][]const u8 = .{ line1, line2 };
1337 var read_vecs: [2][]u8 = .{ &buf2, &buf1 };
13501338
13511339 var src_file = try tmp.dir.createFile("test.txt", .{ .read = true });
13521340 defer src_file.close();
13531341
1354 try src_file.writevAll(&write_vecs);
1342 var writer = src_file.writerStreaming(&.{});
1343
1344 try writer.interface.writeVecAll(&write_vecs);
1345 try writer.interface.flush();
13551346 try testing.expectEqual(@as(u64, line1.len + line2.len), try src_file.getEndPos());
1356 try src_file.seekTo(0);
1357 const read = try src_file.readvAll(&read_vecs);
1358 try testing.expectEqual(@as(usize, line1.len + line2.len), read);
1347
1348 var reader = writer.moveToReader(io);
1349 try reader.seekTo(0);
1350 try reader.interface.readVecAll(&read_vecs);
13591351 try testing.expectEqualStrings(&buf1, "line2\n");
13601352 try testing.expectEqualStrings(&buf2, "line1\n");
1353 try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));
13611354}
13621355
13631356test "pwritev, preadv" {
1357 const io = testing.io;
1358
13641359 var tmp = tmpDir(.{});
13651360 defer tmp.cleanup();
13661361
13671362 const line1 = "line1\n";
13681363 const line2 = "line2\n";
1369
1364 var lines: [2][]const u8 = .{ line1, line2 };
13701365 var buf1: [line1.len]u8 = undefined;
13711366 var buf2: [line2.len]u8 = undefined;
1372 var write_vecs = [_]posix.iovec_const{
1373 .{
1374 .base = line1,
1375 .len = line1.len,
1376 },
1377 .{
1378 .base = line2,
1379 .len = line2.len,
1380 },
1381 };
1382 var read_vecs = [_]posix.iovec{
1383 .{
1384 .base = &buf2,
1385 .len = buf2.len,
1386 },
1387 .{
1388 .base = &buf1,
1389 .len = buf1.len,
1390 },
1391 };
1367 var read_vecs: [2][]u8 = .{ &buf2, &buf1 };
13921368
13931369 var src_file = try tmp.dir.createFile("test.txt", .{ .read = true });
13941370 defer src_file.close();
13951371
1396 try src_file.pwritevAll(&write_vecs, 16);
1372 var writer = src_file.writer(&.{});
1373
1374 try writer.seekTo(16);
1375 try writer.interface.writeVecAll(&lines);
1376 try writer.interface.flush();
13971377 try testing.expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.getEndPos());
1398 const read = try src_file.preadvAll(&read_vecs, 16);
1399 try testing.expectEqual(@as(usize, line1.len + line2.len), read);
1378
1379 var reader = writer.moveToReader(io);
1380 try reader.seekTo(16);
1381 try reader.interface.readVecAll(&read_vecs);
14001382 try testing.expectEqualStrings(&buf1, "line2\n");
14011383 try testing.expectEqualStrings(&buf2, "line1\n");
1384 try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));
14021385}
14031386
14041387test "setEndPos" {
......@@ -1406,6 +1389,8 @@ test "setEndPos" {
14061389 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
14071390 if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23806
14081391
1392 const io = testing.io;
1393
14091394 var tmp = tmpDir(.{});
14101395 defer tmp.cleanup();
14111396
......@@ -1416,11 +1401,13 @@ test "setEndPos" {
14161401
14171402 const initial_size = try f.getEndPos();
14181403 var buffer: [32]u8 = undefined;
1404 var reader = f.reader(io, &.{});
14191405
14201406 {
14211407 try f.setEndPos(initial_size);
14221408 try testing.expectEqual(initial_size, try f.getEndPos());
1423 try testing.expectEqual(initial_size, try f.preadAll(&buffer, 0));
1409 try reader.seekTo(0);
1410 try testing.expectEqual(initial_size, reader.interface.readSliceShort(&buffer));
14241411 try testing.expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]);
14251412 }
14261413
......@@ -1428,7 +1415,8 @@ test "setEndPos" {
14281415 const larger = initial_size + 4;
14291416 try f.setEndPos(larger);
14301417 try testing.expectEqual(larger, try f.getEndPos());
1431 try testing.expectEqual(larger, try f.preadAll(&buffer, 0));
1418 try reader.seekTo(0);
1419 try testing.expectEqual(larger, reader.interface.readSliceShort(&buffer));
14321420 try testing.expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]);
14331421 }
14341422
......@@ -1436,25 +1424,21 @@ test "setEndPos" {
14361424 const smaller = initial_size - 5;
14371425 try f.setEndPos(smaller);
14381426 try testing.expectEqual(smaller, try f.getEndPos());
1439 try testing.expectEqual(smaller, try f.preadAll(&buffer, 0));
1427 try reader.seekTo(0);
1428 try testing.expectEqual(smaller, try reader.interface.readSliceShort(&buffer));
14401429 try testing.expectEqualStrings("nine", buffer[0..@intCast(smaller)]);
14411430 }
14421431
14431432 try f.setEndPos(0);
14441433 try testing.expectEqual(0, try f.getEndPos());
1445 try testing.expectEqual(0, try f.preadAll(&buffer, 0));
1434 try reader.seekTo(0);
1435 try testing.expectEqual(0, try reader.interface.readSliceShort(&buffer));
14461436
14471437 // Invalid file length should error gracefully. Actual limit is host
14481438 // and file-system dependent, but 1PB should fail on filesystems like
14491439 // EXT4 and NTFS. But XFS or Btrfs support up to 8EiB files.
1450 f.setEndPos(0x4_0000_0000_0000) catch |err| if (err != error.FileTooBig) {
1451 return err;
1452 };
1453
1454 f.setEndPos(std.math.maxInt(u63)) catch |err| if (err != error.FileTooBig) {
1455 return err;
1456 };
1457
1440 try testing.expectError(error.FileTooBig, f.setEndPos(0x4_0000_0000_0000));
1441 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63)));
14581442 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63) + 1));
14591443 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u64)));
14601444}
......@@ -1560,31 +1544,6 @@ test "sendfile with buffered data" {
15601544 try std.testing.expectEqualSlices(u8, "AAAA", written_buf[0..amt]);
15611545}
15621546
1563test "copyRangeAll" {
1564 var tmp = tmpDir(.{});
1565 defer tmp.cleanup();
1566
1567 try tmp.dir.makePath("os_test_tmp");
1568
1569 var dir = try tmp.dir.openDir("os_test_tmp", .{});
1570 defer dir.close();
1571
1572 var src_file = try dir.createFile("file1.txt", .{ .read = true });
1573 defer src_file.close();
1574
1575 const data = "u6wj+JmdF3qHsFPE BUlH2g4gJCmEz0PP";
1576 try src_file.writeAll(data);
1577
1578 var dest_file = try dir.createFile("file2.txt", .{ .read = true });
1579 defer dest_file.close();
1580
1581 var written_buf: [100]u8 = undefined;
1582 _ = try src_file.copyRangeAll(0, dest_file, 0, data.len);
1583
1584 const amt = try dest_file.preadAll(&written_buf, 0);
1585 try testing.expectEqualStrings(data, written_buf[0..amt]);
1586}
1587
15881547test "copyFile" {
15891548 try testWithAllSupportedPathTypes(struct {
15901549 fn impl(ctx: *TestContext) !void {
......@@ -1708,8 +1667,8 @@ test "open file with exclusive lock twice, make sure second lock waits" {
17081667 }
17091668 };
17101669
1711 var started = std.Thread.ResetEvent{};
1712 var locked = std.Thread.ResetEvent{};
1670 var started: std.Thread.ResetEvent = .unset;
1671 var locked: std.Thread.ResetEvent = .unset;
17131672
17141673 const t = try std.Thread.spawn(.{}, S.checkFn, .{
17151674 &ctx.dir,
......@@ -1773,7 +1732,7 @@ test "read from locked file" {
17731732 const f = try ctx.dir.createFile(filename, .{ .read = true });
17741733 defer f.close();
17751734 var buffer: [1]u8 = undefined;
1776 _ = try f.readAll(&buffer);
1735 _ = try f.read(&buffer);
17771736 }
17781737 {
17791738 const f = try ctx.dir.createFile(filename, .{
......@@ -1785,9 +1744,9 @@ test "read from locked file" {
17851744 defer f2.close();
17861745 var buffer: [1]u8 = undefined;
17871746 if (builtin.os.tag == .windows) {
1788 try std.testing.expectError(error.LockViolation, f2.readAll(&buffer));
1747 try std.testing.expectError(error.LockViolation, f2.read(&buffer));
17891748 } else {
1790 try std.testing.expectEqual(0, f2.readAll(&buffer));
1749 try std.testing.expectEqual(0, f2.read(&buffer));
17911750 }
17921751 }
17931752 }
......@@ -1944,6 +1903,7 @@ test "'.' and '..' in fs.Dir functions" {
19441903
19451904 try testWithAllSupportedPathTypes(struct {
19461905 fn impl(ctx: *TestContext) !void {
1906 const io = ctx.io;
19471907 const subdir_path = try ctx.transformPath("./subdir");
19481908 const file_path = try ctx.transformPath("./subdir/../file");
19491909 const copy_path = try ctx.transformPath("./subdir/../copy");
......@@ -1966,7 +1926,8 @@ test "'.' and '..' in fs.Dir functions" {
19661926 try ctx.dir.deleteFile(rename_path);
19671927
19681928 try ctx.dir.writeFile(.{ .sub_path = update_path, .data = "something" });
1969 const prev_status = try ctx.dir.updateFile(file_path, ctx.dir, update_path, .{});
1929 var dir = ctx.dir.adaptToNewApi();
1930 const prev_status = try dir.updateFile(io, file_path, dir, update_path, .{});
19701931 try testing.expectEqual(fs.Dir.PrevStatus.stale, prev_status);
19711932
19721933 try ctx.dir.deleteDir(subdir_path);
......@@ -2005,13 +1966,6 @@ test "'.' and '..' in absolute functions" {
20051966 renamed_file.close();
20061967 try fs.deleteFileAbsolute(renamed_file_path);
20071968
2008 const update_file_path = try fs.path.join(allocator, &.{ subdir_path, "../update" });
2009 const update_file = try fs.createFileAbsolute(update_file_path, .{});
2010 try update_file.writeAll("something");
2011 update_file.close();
2012 const prev_status = try fs.updateFileAbsolute(created_file_path, update_file_path, .{});
2013 try testing.expectEqual(fs.Dir.PrevStatus.stale, prev_status);
2014
20151969 try fs.deleteDirAbsolute(subdir_path);
20161970}
20171971
......@@ -2079,6 +2033,7 @@ test "invalid UTF-8/WTF-8 paths" {
20792033
20802034 try testWithAllSupportedPathTypes(struct {
20812035 fn impl(ctx: *TestContext) !void {
2036 const io = ctx.io;
20822037 // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte
20832038 const invalid_path = try ctx.transformPath("\xFF");
20842039
......@@ -2129,7 +2084,8 @@ test "invalid UTF-8/WTF-8 paths" {
21292084 try testing.expectError(expected_err, ctx.dir.access(invalid_path, .{}));
21302085 try testing.expectError(expected_err, ctx.dir.accessZ(invalid_path, .{}));
21312086
2132 try testing.expectError(expected_err, ctx.dir.updateFile(invalid_path, ctx.dir, invalid_path, .{}));
2087 var dir = ctx.dir.adaptToNewApi();
2088 try testing.expectError(expected_err, dir.updateFile(io, invalid_path, dir, invalid_path, .{}));
21332089 try testing.expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, .{}));
21342090
21352091 try testing.expectError(expected_err, ctx.dir.statFile(invalid_path));
......@@ -2144,7 +2100,6 @@ test "invalid UTF-8/WTF-8 paths" {
21442100 try testing.expectError(expected_err, fs.renameZ(ctx.dir, invalid_path, ctx.dir, invalid_path));
21452101
21462102 if (native_os != .wasi and ctx.path_type != .relative) {
2147 try testing.expectError(expected_err, fs.updateFileAbsolute(invalid_path, invalid_path, .{}));
21482103 try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{}));
21492104 try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path));
21502105 try testing.expectError(expected_err, fs.makeDirAbsoluteZ(invalid_path));
......@@ -2175,6 +2130,8 @@ test "invalid UTF-8/WTF-8 paths" {
21752130}
21762131
21772132test "read file non vectored" {
2133 const io = std.testing.io;
2134
21782135 var tmp_dir = testing.tmpDir(.{});
21792136 defer tmp_dir.cleanup();
21802137
......@@ -2188,7 +2145,7 @@ test "read file non vectored" {
21882145 try file_writer.interface.flush();
21892146 }
21902147
2191 var file_reader: std.fs.File.Reader = .init(file, &.{});
2148 var file_reader: std.Io.File.Reader = .initAdapted(file, io, &.{});
21922149
21932150 var write_buffer: [100]u8 = undefined;
21942151 var w: std.Io.Writer = .fixed(&write_buffer);
......@@ -2205,6 +2162,8 @@ test "read file non vectored" {
22052162}
22062163
22072164test "seek keeping partial buffer" {
2165 const io = std.testing.io;
2166
22082167 var tmp_dir = testing.tmpDir(.{});
22092168 defer tmp_dir.cleanup();
22102169
......@@ -2219,7 +2178,7 @@ test "seek keeping partial buffer" {
22192178 }
22202179
22212180 var read_buffer: [3]u8 = undefined;
2222 var file_reader: std.fs.File.Reader = .init(file, &read_buffer);
2181 var file_reader: Io.File.Reader = .initAdapted(file, io, &read_buffer);
22232182
22242183 try testing.expectEqual(0, file_reader.logicalPos());
22252184
......@@ -2246,13 +2205,15 @@ test "seek keeping partial buffer" {
22462205}
22472206
22482207test "seekBy" {
2208 const io = testing.io;
2209
22492210 var tmp_dir = testing.tmpDir(.{});
22502211 defer tmp_dir.cleanup();
22512212
22522213 try tmp_dir.dir.writeFile(.{ .sub_path = "blah.txt", .data = "let's test seekBy" });
22532214 const f = try tmp_dir.dir.openFile("blah.txt", .{ .mode = .read_only });
22542215 defer f.close();
2255 var reader = f.readerStreaming(&.{});
2216 var reader = f.readerStreaming(io, &.{});
22562217 try reader.seekBy(2);
22572218
22582219 var buffer: [20]u8 = undefined;
lib/std/http/Client.zig+20-25
......@@ -247,6 +247,7 @@ pub const Connection = struct {
247247 port: u16,
248248 stream: Io.net.Stream,
249249 ) error{OutOfMemory}!*Plain {
250 const io = client.io;
250251 const gpa = client.allocator;
251252 const alloc_len = allocLen(client, remote_host.bytes.len);
252253 const base = try gpa.alignedAlloc(u8, .of(Plain), alloc_len);
......@@ -260,8 +261,8 @@ pub const Connection = struct {
260261 plain.* = .{
261262 .connection = .{
262263 .client = client,
263 .stream_writer = stream.writer(socket_write_buffer),
264 .stream_reader = stream.reader(socket_read_buffer),
264 .stream_writer = stream.writer(io, socket_write_buffer),
265 .stream_reader = stream.reader(io, socket_read_buffer),
265266 .pool_node = .{},
266267 .port = port,
267268 .host_len = @intCast(remote_host.bytes.len),
......@@ -300,6 +301,7 @@ pub const Connection = struct {
300301 port: u16,
301302 stream: Io.net.Stream,
302303 ) error{ OutOfMemory, TlsInitializationFailed }!*Tls {
304 const io = client.io;
303305 const gpa = client.allocator;
304306 const alloc_len = allocLen(client, remote_host.bytes.len);
305307 const base = try gpa.alignedAlloc(u8, .of(Tls), alloc_len);
......@@ -316,11 +318,14 @@ pub const Connection = struct {
316318 assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);
317319 @memcpy(host_buffer, remote_host.bytes);
318320 const tls: *Tls = @ptrCast(base);
321 var random_buffer: [176]u8 = undefined;
322 std.crypto.random.bytes(&random_buffer);
323 const now_ts = if (Io.Timestamp.now(io, .real)) |ts| ts.toSeconds() else |_| return error.TlsInitializationFailed;
319324 tls.* = .{
320325 .connection = .{
321326 .client = client,
322 .stream_writer = stream.writer(tls_write_buffer),
323 .stream_reader = stream.reader(socket_read_buffer),
327 .stream_writer = stream.writer(io, tls_write_buffer),
328 .stream_reader = stream.reader(io, socket_read_buffer),
324329 .pool_node = .{},
325330 .port = port,
326331 .host_len = @intCast(remote_host.bytes.len),
......@@ -338,6 +343,8 @@ pub const Connection = struct {
338343 .ssl_key_log = client.ssl_key_log,
339344 .read_buffer = tls_read_buffer,
340345 .write_buffer = socket_write_buffer,
346 .entropy = &random_buffer,
347 .realtime_now_seconds = now_ts,
341348 // This is appropriate for HTTPS because the HTTP headers contain
342349 // the content length which is used to detect truncation attacks.
343350 .allow_truncation_attacks = true,
......@@ -1390,16 +1397,8 @@ pub const basic_authorization = struct {
13901397};
13911398
13921399pub const ConnectTcpError = error{
1393 ConnectionRefused,
1394 NetworkUnreachable,
1395 ConnectionTimedOut,
1396 ConnectionResetByPeer,
1397 TemporaryNameServerFailure,
1398 NameServerFailure,
1399 UnknownHostName,
1400 UnexpectedConnectFailure,
14011400 TlsInitializationFailed,
1402} || Allocator.Error || Io.Cancelable;
1401} || Allocator.Error || HostName.ConnectError;
14031402
14041403/// Reuses a `Connection` if one matching `host` and `port` is already open.
14051404///
......@@ -1424,6 +1423,7 @@ pub const ConnectTcpOptions = struct {
14241423};
14251424
14261425pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcpError!*Connection {
1426 const io = client.io;
14271427 const host = options.host;
14281428 const port = options.port;
14291429 const protocol = options.protocol;
......@@ -1437,22 +1437,17 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp
14371437 .protocol = protocol,
14381438 })) |conn| return conn;
14391439
1440 const stream = host.connect(client.io, port, .{ .mode = .stream }) catch |err| switch (err) {
1441 error.ConnectionRefused => return error.ConnectionRefused,
1442 error.NetworkUnreachable => return error.NetworkUnreachable,
1443 error.ConnectionTimedOut => return error.ConnectionTimedOut,
1444 error.ConnectionResetByPeer => return error.ConnectionResetByPeer,
1445 error.NameServerFailure => return error.NameServerFailure,
1446 error.UnknownHostName => return error.UnknownHostName,
1447 error.Canceled => return error.Canceled,
1448 //else => return error.UnexpectedConnectFailure,
1449 };
1450 errdefer stream.close();
1440 var stream = try host.connect(io, port, .{ .mode = .stream });
1441 errdefer stream.close(io);
14511442
14521443 switch (protocol) {
14531444 .tls => {
14541445 if (disable_tls) return error.TlsInitializationFailed;
1455 const tc = try Connection.Tls.create(client, proxied_host, proxied_port, stream);
1446 const tc = Connection.Tls.create(client, proxied_host, proxied_port, stream) catch |err| switch (err) {
1447 error.OutOfMemory => |e| return e,
1448 error.Unexpected => |e| return e,
1449 error.UnsupportedClock => return error.TlsInitializationFailed,
1450 };
14561451 client.connection_pool.addUsed(&tc.connection);
14571452 return &tc.connection;
14581453 },
lib/std/os/linux/IoUring.zig+90-72
......@@ -3,7 +3,7 @@ const std = @import("std");
33const builtin = @import("builtin");
44const assert = std.debug.assert;
55const mem = std.mem;
6const net = std.net;
6const net = std.Io.net;
77const posix = std.posix;
88const linux = std.os.linux;
99const testing = std.testing;
......@@ -2361,19 +2361,22 @@ test "sendmsg/recvmsg" {
23612361 };
23622362 defer ring.deinit();
23632363
2364 var address_server = try net.Address.parseIp4("127.0.0.1", 0);
2364 var address_server: linux.sockaddr.in = .{
2365 .port = 0,
2366 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
2367 };
23652368
2366 const server = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0);
2369 const server = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0);
23672370 defer posix.close(server);
23682371 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
23692372 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2370 try posix.bind(server, &address_server.any, address_server.getOsSockLen());
2373 try posix.bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in));
23712374
23722375 // set address_server to the OS-chosen IP/port.
2373 var slen: posix.socklen_t = address_server.getOsSockLen();
2374 try posix.getsockname(server, &address_server.any, &slen);
2376 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
2377 try posix.getsockname(server, addrAny(&address_server), &slen);
23752378
2376 const client = try posix.socket(address_server.any.family, posix.SOCK.DGRAM, 0);
2379 const client = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0);
23772380 defer posix.close(client);
23782381
23792382 const buffer_send = [_]u8{42} ** 128;
......@@ -2381,8 +2384,8 @@ test "sendmsg/recvmsg" {
23812384 posix.iovec_const{ .base = &buffer_send, .len = buffer_send.len },
23822385 };
23832386 const msg_send: posix.msghdr_const = .{
2384 .name = &address_server.any,
2385 .namelen = address_server.getOsSockLen(),
2387 .name = addrAny(&address_server),
2388 .namelen = @sizeOf(linux.sockaddr.in),
23862389 .iov = &iovecs_send,
23872390 .iovlen = 1,
23882391 .control = null,
......@@ -2398,11 +2401,13 @@ test "sendmsg/recvmsg" {
23982401 var iovecs_recv = [_]posix.iovec{
23992402 posix.iovec{ .base = &buffer_recv, .len = buffer_recv.len },
24002403 };
2401 const addr = [_]u8{0} ** 4;
2402 var address_recv = net.Address.initIp4(addr, 0);
2404 var address_recv: linux.sockaddr.in = .{
2405 .port = 0,
2406 .addr = 0,
2407 };
24032408 var msg_recv: posix.msghdr = .{
2404 .name = &address_recv.any,
2405 .namelen = address_recv.getOsSockLen(),
2409 .name = addrAny(&address_recv),
2410 .namelen = @sizeOf(linux.sockaddr.in),
24062411 .iov = &iovecs_recv,
24072412 .iovlen = 1,
24082413 .control = null,
......@@ -2441,6 +2446,8 @@ test "sendmsg/recvmsg" {
24412446test "timeout (after a relative time)" {
24422447 if (!is_linux) return error.SkipZigTest;
24432448
2449 const io = testing.io;
2450
24442451 var ring = IoUring.init(1, 0) catch |err| switch (err) {
24452452 error.SystemOutdated => return error.SkipZigTest,
24462453 error.PermissionDenied => return error.SkipZigTest,
......@@ -2452,12 +2459,12 @@ test "timeout (after a relative time)" {
24522459 const margin = 5;
24532460 const ts: linux.kernel_timespec = .{ .sec = 0, .nsec = ms * 1000000 };
24542461
2455 const started = std.time.milliTimestamp();
2462 const started = try std.Io.Timestamp.now(io, .awake);
24562463 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);
24572464 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe.opcode);
24582465 try testing.expectEqual(@as(u32, 1), try ring.submit());
24592466 const cqe = try ring.copy_cqe();
2460 const stopped = std.time.milliTimestamp();
2467 const stopped = try std.Io.Timestamp.now(io, .awake);
24612468
24622469 try testing.expectEqual(linux.io_uring_cqe{
24632470 .user_data = 0x55555555,
......@@ -2466,7 +2473,8 @@ test "timeout (after a relative time)" {
24662473 }, cqe);
24672474
24682475 // Tests should not depend on timings: skip test if outside margin.
2469 if (!std.math.approxEqAbs(f64, ms, @as(f64, @floatFromInt(stopped - started)), margin)) return error.SkipZigTest;
2476 const ms_elapsed = started.durationTo(stopped).toMilliseconds();
2477 if (ms_elapsed > margin) return error.SkipZigTest;
24702478}
24712479
24722480test "timeout (after a number of completions)" {
......@@ -2861,19 +2869,22 @@ test "shutdown" {
28612869 };
28622870 defer ring.deinit();
28632871
2864 var address = try net.Address.parseIp4("127.0.0.1", 0);
2872 var address: linux.sockaddr.in = .{
2873 .port = 0,
2874 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
2875 };
28652876
28662877 // Socket bound, expect shutdown to work
28672878 {
2868 const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2879 const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
28692880 defer posix.close(server);
28702881 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
2871 try posix.bind(server, &address.any, address.getOsSockLen());
2882 try posix.bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in));
28722883 try posix.listen(server, 1);
28732884
28742885 // set address to the OS-chosen IP/port.
2875 var slen: posix.socklen_t = address.getOsSockLen();
2876 try posix.getsockname(server, &address.any, &slen);
2886 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
2887 try posix.getsockname(server, addrAny(&address), &slen);
28772888
28782889 const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD);
28792890 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
......@@ -2898,7 +2909,7 @@ test "shutdown" {
28982909
28992910 // Socket not bound, expect to fail with ENOTCONN
29002911 {
2901 const server = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
2912 const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
29022913 defer posix.close(server);
29032914
29042915 const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {
......@@ -2966,22 +2977,11 @@ test "renameat" {
29662977 }, cqe);
29672978
29682979 // Validate that the old file doesn't exist anymore
2969 {
2970 _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) {
2971 error.FileNotFound => {},
2972 else => std.debug.panic("unexpected error: {}", .{err}),
2973 };
2974 }
2980 try testing.expectError(error.FileNotFound, tmp.dir.openFile(old_path, .{}));
29752981
29762982 // Validate that the new file exists with the proper content
2977 {
2978 const new_file = try tmp.dir.openFile(new_path, .{});
2979 defer new_file.close();
2980
2981 var new_file_data: [16]u8 = undefined;
2982 const bytes_read = try new_file.readAll(&new_file_data);
2983 try testing.expectEqualStrings("hello", new_file_data[0..bytes_read]);
2984 }
2983 var new_file_data: [16]u8 = undefined;
2984 try testing.expectEqualStrings("hello", try tmp.dir.readFile(new_path, &new_file_data));
29852985}
29862986
29872987test "unlinkat" {
......@@ -3179,12 +3179,8 @@ test "linkat" {
31793179 }, cqe);
31803180
31813181 // Validate the second file
3182 const second_file = try tmp.dir.openFile(second_path, .{});
3183 defer second_file.close();
3184
31853182 var second_file_data: [16]u8 = undefined;
3186 const bytes_read = try second_file.readAll(&second_file_data);
3187 try testing.expectEqualStrings("hello", second_file_data[0..bytes_read]);
3183 try testing.expectEqualStrings("hello", try tmp.dir.readFile(second_path, &second_file_data));
31883184}
31893185
31903186test "provide_buffers: read" {
......@@ -3588,7 +3584,10 @@ const SocketTestHarness = struct {
35883584
35893585fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
35903586 // Create a TCP server socket
3591 var address = try net.Address.parseIp4("127.0.0.1", 0);
3587 var address: linux.sockaddr.in = .{
3588 .port = 0,
3589 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
3590 };
35923591 const listener_socket = try createListenerSocket(&address);
35933592 errdefer posix.close(listener_socket);
35943593
......@@ -3598,9 +3597,9 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
35983597 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);
35993598
36003599 // Create a TCP client socket
3601 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3600 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
36023601 errdefer posix.close(client);
3603 _ = try ring.connect(0xcccccccc, client, &address.any, address.getOsSockLen());
3602 _ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in));
36043603
36053604 try testing.expectEqual(@as(u32, 2), try ring.submit());
36063605
......@@ -3636,18 +3635,18 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
36363635 };
36373636}
36383637
3639fn createListenerSocket(address: *net.Address) !posix.socket_t {
3638fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t {
36403639 const kernel_backlog = 1;
3641 const listener_socket = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3640 const listener_socket = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
36423641 errdefer posix.close(listener_socket);
36433642
36443643 try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
3645 try posix.bind(listener_socket, &address.any, address.getOsSockLen());
3644 try posix.bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in));
36463645 try posix.listen(listener_socket, kernel_backlog);
36473646
36483647 // set address to the OS-chosen IP/port.
3649 var slen: posix.socklen_t = address.getOsSockLen();
3650 try posix.getsockname(listener_socket, &address.any, &slen);
3648 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
3649 try posix.getsockname(listener_socket, addrAny(address), &slen);
36513650
36523651 return listener_socket;
36533652}
......@@ -3662,7 +3661,10 @@ test "accept multishot" {
36623661 };
36633662 defer ring.deinit();
36643663
3665 var address = try net.Address.parseIp4("127.0.0.1", 0);
3664 var address: linux.sockaddr.in = .{
3665 .port = 0,
3666 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
3667 };
36663668 const listener_socket = try createListenerSocket(&address);
36673669 defer posix.close(listener_socket);
36683670
......@@ -3676,9 +3678,9 @@ test "accept multishot" {
36763678 var nr: usize = 4; // number of clients to connect
36773679 while (nr > 0) : (nr -= 1) {
36783680 // connect client
3679 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3681 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
36803682 errdefer posix.close(client);
3681 try posix.connect(client, &address.any, address.getOsSockLen());
3683 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
36823684
36833685 // test accept completion
36843686 var cqe = try ring.copy_cqe();
......@@ -3756,7 +3758,10 @@ test "accept_direct" {
37563758 else => return err,
37573759 };
37583760 defer ring.deinit();
3759 var address = try net.Address.parseIp4("127.0.0.1", 0);
3761 var address: linux.sockaddr.in = .{
3762 .port = 0,
3763 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
3764 };
37603765
37613766 // register direct file descriptors
37623767 var registered_fds = [_]posix.fd_t{-1} ** 2;
......@@ -3779,8 +3784,8 @@ test "accept_direct" {
37793784 try testing.expectEqual(@as(u32, 1), try ring.submit());
37803785
37813786 // connect
3782 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3783 try posix.connect(client, &address.any, address.getOsSockLen());
3787 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3788 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
37843789 defer posix.close(client);
37853790
37863791 // accept completion
......@@ -3813,8 +3818,8 @@ test "accept_direct" {
38133818 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);
38143819 try testing.expectEqual(@as(u32, 1), try ring.submit());
38153820 // connect
3816 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3817 try posix.connect(client, &address.any, address.getOsSockLen());
3821 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3822 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
38183823 defer posix.close(client);
38193824 // completion with error
38203825 const cqe_accept = try ring.copy_cqe();
......@@ -3837,7 +3842,10 @@ test "accept_multishot_direct" {
38373842 };
38383843 defer ring.deinit();
38393844
3840 var address = try net.Address.parseIp4("127.0.0.1", 0);
3845 var address: linux.sockaddr.in = .{
3846 .port = 0,
3847 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
3848 };
38413849
38423850 var registered_fds = [_]posix.fd_t{-1} ** 2;
38433851 try ring.register_files(registered_fds[0..]);
......@@ -3855,8 +3863,8 @@ test "accept_multishot_direct" {
38553863
38563864 for (registered_fds) |_| {
38573865 // connect
3858 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3859 try posix.connect(client, &address.any, address.getOsSockLen());
3866 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3867 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
38603868 defer posix.close(client);
38613869
38623870 // accept completion
......@@ -3870,8 +3878,8 @@ test "accept_multishot_direct" {
38703878 // Multishot is terminated (more flag is not set).
38713879 {
38723880 // connect
3873 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3874 try posix.connect(client, &address.any, address.getOsSockLen());
3881 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3882 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
38753883 defer posix.close(client);
38763884 // completion with error
38773885 const cqe_accept = try ring.copy_cqe();
......@@ -3944,7 +3952,10 @@ test "socket_direct/socket_direct_alloc/close_direct" {
39443952 try testing.expect(cqe_socket.res == 2); // returns registered file index
39453953
39463954 // use sockets from registered_fds in connect operation
3947 var address = try net.Address.parseIp4("127.0.0.1", 0);
3955 var address: linux.sockaddr.in = .{
3956 .port = 0,
3957 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
3958 };
39483959 const listener_socket = try createListenerSocket(&address);
39493960 defer posix.close(listener_socket);
39503961 const accept_userdata: u64 = 0xaaaaaaaa;
......@@ -3954,7 +3965,7 @@ test "socket_direct/socket_direct_alloc/close_direct" {
39543965 // prepare accept
39553966 _ = try ring.accept(accept_userdata, listener_socket, null, null, 0);
39563967 // prepare connect with fixed socket
3957 const connect_sqe = try ring.connect(connect_userdata, @intCast(fd_index), &address.any, address.getOsSockLen());
3968 const connect_sqe = try ring.connect(connect_userdata, @intCast(fd_index), addrAny(&address), @sizeOf(linux.sockaddr.in));
39583969 connect_sqe.flags |= linux.IOSQE_FIXED_FILE; // fd is fixed file index
39593970 // submit both
39603971 try testing.expectEqual(@as(u32, 2), try ring.submit());
......@@ -4483,12 +4494,15 @@ test "bind/listen/connect" {
44834494 // LISTEN is higher required operation
44844495 if (!probe.is_supported(.LISTEN)) return error.SkipZigTest;
44854496
4486 var addr = net.Address.initIp4([4]u8{ 127, 0, 0, 1 }, 0);
4487 const proto: u32 = if (addr.any.family == linux.AF.UNIX) 0 else linux.IPPROTO.TCP;
4497 var addr: linux.sockaddr.in = .{
4498 .port = 0,
4499 .addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
4500 };
4501 const proto: u32 = if (addr.family == linux.AF.UNIX) 0 else linux.IPPROTO.TCP;
44884502
44894503 const listen_fd = brk: {
44904504 // Create socket
4491 _ = try ring.socket(1, addr.any.family, linux.SOCK.STREAM | linux.SOCK.CLOEXEC, proto, 0);
4505 _ = try ring.socket(1, addr.family, linux.SOCK.STREAM | linux.SOCK.CLOEXEC, proto, 0);
44924506 try testing.expectEqual(1, try ring.submit());
44934507 var cqe = try ring.copy_cqe();
44944508 try testing.expectEqual(1, cqe.user_data);
......@@ -4500,7 +4514,7 @@ test "bind/listen/connect" {
45004514 var optval: u32 = 1;
45014515 (try ring.setsockopt(2, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval))).link_next();
45024516 (try ring.setsockopt(3, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEPORT, mem.asBytes(&optval))).link_next();
4503 (try ring.bind(4, listen_fd, &addr.any, addr.getOsSockLen(), 0)).link_next();
4517 (try ring.bind(4, listen_fd, addrAny(&addr), @sizeOf(linux.sockaddr.in), 0)).link_next();
45044518 _ = try ring.listen(5, listen_fd, 1, 0);
45054519 // Submit 4 operations
45064520 try testing.expectEqual(4, try ring.submit());
......@@ -4521,15 +4535,15 @@ test "bind/listen/connect" {
45214535 try testing.expectEqual(1, optval);
45224536
45234537 // Read system assigned port into addr
4524 var addr_len: posix.socklen_t = addr.getOsSockLen();
4525 try posix.getsockname(listen_fd, &addr.any, &addr_len);
4538 var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in);
4539 try posix.getsockname(listen_fd, addrAny(&addr), &addr_len);
45264540
45274541 break :brk listen_fd;
45284542 };
45294543
45304544 const connect_fd = brk: {
45314545 // Create connect socket
4532 _ = try ring.socket(6, addr.any.family, linux.SOCK.STREAM | linux.SOCK.CLOEXEC, proto, 0);
4546 _ = try ring.socket(6, addr.family, linux.SOCK.STREAM | linux.SOCK.CLOEXEC, proto, 0);
45334547 try testing.expectEqual(1, try ring.submit());
45344548 const cqe = try ring.copy_cqe();
45354549 try testing.expectEqual(6, cqe.user_data);
......@@ -4542,7 +4556,7 @@ test "bind/listen/connect" {
45424556
45434557 // Prepare accept/connect operations
45444558 _ = try ring.accept(7, listen_fd, null, null, 0);
4545 _ = try ring.connect(8, connect_fd, &addr.any, addr.getOsSockLen());
4559 _ = try ring.connect(8, connect_fd, addrAny(&addr), @sizeOf(linux.sockaddr.in));
45464560 try testing.expectEqual(2, try ring.submit());
45474561 // Get listener accepted socket
45484562 var accept_fd: posix.socket_t = 0;
......@@ -4604,3 +4618,7 @@ fn testSendRecv(ring: *IoUring, send_fd: posix.socket_t, recv_fd: posix.socket_t
46044618 try testing.expectEqualSlices(u8, buffer_send, buffer_recv[0..buffer_send.len]);
46054619 try testing.expectEqualSlices(u8, buffer_send, buffer_recv[buffer_send.len..]);
46064620}
4621
4622fn addrAny(addr: *linux.sockaddr.in) *linux.sockaddr {
4623 return @ptrCast(addr);
4624}
lib/std/posix.zig+1-25
......@@ -3000,31 +3000,7 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: mode_t) MakeDirErro
30003000 windows.CloseHandle(sub_dir_handle);
30013001}
30023002
3003pub const MakeDirError = error{
3004 /// In WASI, this error may occur when the file descriptor does
3005 /// not hold the required rights to create a new directory relative to it.
3006 AccessDenied,
3007 PermissionDenied,
3008 DiskQuota,
3009 PathAlreadyExists,
3010 SymLinkLoop,
3011 LinkQuotaExceeded,
3012 NameTooLong,
3013 FileNotFound,
3014 SystemResources,
3015 NoSpaceLeft,
3016 NotDir,
3017 ReadOnlyFileSystem,
3018 /// WASI-only; file paths must be valid UTF-8.
3019 InvalidUtf8,
3020 /// Windows-only; file paths provided by the user must be valid WTF-8.
3021 /// https://wtf-8.codeberg.page/
3022 InvalidWtf8,
3023 BadPathName,
3024 NoDevice,
3025 /// On Windows, `\\server` or `\\server\share` was not found.
3026 NetworkNotFound,
3027} || UnexpectedError;
3003pub const MakeDirError = std.Io.Dir.MakeError;
30283004
30293005/// Create a directory.
30303006/// `mode` is ignored on Windows and WASI.
lib/std/posix/test.zig+2-5
......@@ -731,11 +731,8 @@ test "dup & dup2" {
731731 try dup2ed.writeAll("dup2");
732732 }
733733
734 var file = try tmp.dir.openFile("os_dup_test", .{});
735 defer file.close();
736
737 var buf: [7]u8 = undefined;
738 try testing.expectEqualStrings("dupdup2", buf[0..try file.readAll(&buf)]);
734 var buffer: [8]u8 = undefined;
735 try testing.expectEqualStrings("dupdup2", try tmp.dir.readFile("os_dup_test", &buffer));
739736}
740737
741738test "writev longer than IOV_MAX" {
lib/std/process/Child.zig+26-11
......@@ -1,5 +1,9 @@
1const std = @import("../std.zig");
1const ChildProcess = @This();
2
23const builtin = @import("builtin");
4const native_os = builtin.os.tag;
5
6const std = @import("../std.zig");
37const unicode = std.unicode;
48const fs = std.fs;
59const process = std.process;
......@@ -11,9 +15,7 @@ const mem = std.mem;
1115const EnvMap = std.process.EnvMap;
1216const maxInt = std.math.maxInt;
1317const assert = std.debug.assert;
14const native_os = builtin.os.tag;
1518const Allocator = std.mem.Allocator;
16const ChildProcess = @This();
1719const ArrayList = std.ArrayList;
1820
1921pub const Id = switch (native_os) {
......@@ -317,16 +319,23 @@ pub fn waitForSpawn(self: *ChildProcess) SpawnError!void {
317319
318320 const err_pipe = self.err_pipe orelse return;
319321 self.err_pipe = null;
320
321322 // Wait for the child to report any errors in or before `execvpe`.
322 if (readIntFd(err_pipe)) |child_err_int| {
323 posix.close(err_pipe);
323 const report = readIntFd(err_pipe);
324 posix.close(err_pipe);
325 if (report) |child_err_int| {
324326 const child_err: SpawnError = @errorCast(@errorFromInt(child_err_int));
325327 self.term = child_err;
326328 return child_err;
327 } else |_| {
328 // Write end closed by CLOEXEC at the time of the `execvpe` call, indicating success!
329 posix.close(err_pipe);
329 } else |read_err| switch (read_err) {
330 error.EndOfStream => {
331 // Write end closed by CLOEXEC at the time of the `execvpe` call,
332 // indicating success.
333 },
334 else => {
335 // Problem reading the error from the error reporting pipe. We
336 // don't know if the child is alive or dead. Better to assume it is
337 // alive so the resource does not risk being leaked.
338 },
330339 }
331340}
332341
......@@ -1014,8 +1023,14 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
10141023
10151024fn readIntFd(fd: i32) !ErrInt {
10161025 var buffer: [8]u8 = undefined;
1017 var fr: std.fs.File.Reader = .initStreaming(.{ .handle = fd }, &buffer);
1018 return @intCast(fr.interface.takeInt(u64, .little) catch return error.SystemResources);
1026 var i: usize = 0;
1027 while (i < buffer.len) {
1028 const n = try std.posix.read(fd, buffer[i..]);
1029 if (n == 0) return error.EndOfStream;
1030 i += n;
1031 }
1032 const int = mem.readInt(u64, &buffer, .little);
1033 return @intCast(int);
10191034}
10201035
10211036const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
lib/std/tar/Writer.zig+20-18
......@@ -1,7 +1,9 @@
1const Writer = @This();
2
13const std = @import("std");
4const Io = std.Io;
25const assert = std.debug.assert;
36const testing = std.testing;
4const Writer = @This();
57
68const block_size = @sizeOf(Header);
79
......@@ -14,7 +16,7 @@ pub const Options = struct {
1416 mtime: u64 = 0,
1517};
1618
17underlying_writer: *std.Io.Writer,
19underlying_writer: *Io.Writer,
1820prefix: []const u8 = "",
1921mtime_now: u64 = 0,
2022
......@@ -36,12 +38,12 @@ pub fn writeDir(w: *Writer, sub_path: []const u8, options: Options) Error!void {
3638 try w.writeHeader(.directory, sub_path, "", 0, options);
3739}
3840
39pub const WriteFileError = std.Io.Writer.FileError || Error || std.fs.File.Reader.SizeError;
41pub const WriteFileError = Io.Writer.FileError || Error || Io.File.Reader.SizeError;
4042
4143pub fn writeFile(
4244 w: *Writer,
4345 sub_path: []const u8,
44 file_reader: *std.fs.File.Reader,
46 file_reader: *Io.File.Reader,
4547 stat_mtime: i128,
4648) WriteFileError!void {
4749 const size = try file_reader.getSize();
......@@ -58,7 +60,7 @@ pub fn writeFile(
5860 try w.writePadding64(size);
5961}
6062
61pub const WriteFileStreamError = Error || std.Io.Reader.StreamError;
63pub const WriteFileStreamError = Error || Io.Reader.StreamError;
6264
6365/// Writes file reading file content from `reader`. Reads exactly `size` bytes
6466/// from `reader`, or returns `error.EndOfStream`.
......@@ -66,7 +68,7 @@ pub fn writeFileStream(
6668 w: *Writer,
6769 sub_path: []const u8,
6870 size: u64,
69 reader: *std.Io.Reader,
71 reader: *Io.Reader,
7072 options: Options,
7173) WriteFileStreamError!void {
7274 try w.writeHeader(.regular, sub_path, "", size, options);
......@@ -136,15 +138,15 @@ fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const [
136138 try w.writePadding(len);
137139}
138140
139fn writePadding(w: *Writer, bytes: usize) std.Io.Writer.Error!void {
141fn writePadding(w: *Writer, bytes: usize) Io.Writer.Error!void {
140142 return writePaddingPos(w, bytes % block_size);
141143}
142144
143fn writePadding64(w: *Writer, bytes: u64) std.Io.Writer.Error!void {
145fn writePadding64(w: *Writer, bytes: u64) Io.Writer.Error!void {
144146 return writePaddingPos(w, @intCast(bytes % block_size));
145147}
146148
147fn writePaddingPos(w: *Writer, pos: usize) std.Io.Writer.Error!void {
149fn writePaddingPos(w: *Writer, pos: usize) Io.Writer.Error!void {
148150 if (pos == 0) return;
149151 try w.underlying_writer.splatByteAll(0, block_size - pos);
150152}
......@@ -153,7 +155,7 @@ fn writePaddingPos(w: *Writer, pos: usize) std.Io.Writer.Error!void {
153155/// "reasonable system must not assume that such a block exists when reading an
154156/// archive". Therefore, the Zig standard library recommends to not call this
155157/// function.
156pub fn finishPedantically(w: *Writer) std.Io.Writer.Error!void {
158pub fn finishPedantically(w: *Writer) Io.Writer.Error!void {
157159 try w.underlying_writer.splatByteAll(0, block_size * 2);
158160}
159161
......@@ -248,7 +250,7 @@ pub const Header = extern struct {
248250 try octal(&w.checksum, checksum);
249251 }
250252
251 pub fn write(h: *Header, bw: *std.Io.Writer) error{ OctalOverflow, WriteFailed }!void {
253 pub fn write(h: *Header, bw: *Io.Writer) error{ OctalOverflow, WriteFailed }!void {
252254 try h.updateChecksum();
253255 try bw.writeAll(std.mem.asBytes(h));
254256 }
......@@ -396,14 +398,14 @@ test "write files" {
396398 {
397399 const root = "root";
398400
399 var output: std.Io.Writer.Allocating = .init(testing.allocator);
401 var output: Io.Writer.Allocating = .init(testing.allocator);
400402 var w: Writer = .{ .underlying_writer = &output.writer };
401403 defer output.deinit();
402404 try w.setRoot(root);
403405 for (files) |file|
404406 try w.writeFileBytes(file.path, file.content, .{});
405407
406 var input: std.Io.Reader = .fixed(output.written());
408 var input: Io.Reader = .fixed(output.written());
407409 var it: std.tar.Iterator = .init(&input, .{
408410 .file_name_buffer = &file_name_buffer,
409411 .link_name_buffer = &link_name_buffer,
......@@ -424,7 +426,7 @@ test "write files" {
424426 try testing.expectEqual('/', actual.name[root.len..][0]);
425427 try testing.expectEqualStrings(expected.path, actual.name[root.len + 1 ..]);
426428
427 var content: std.Io.Writer.Allocating = .init(testing.allocator);
429 var content: Io.Writer.Allocating = .init(testing.allocator);
428430 defer content.deinit();
429431 try it.streamRemaining(actual, &content.writer);
430432 try testing.expectEqualSlices(u8, expected.content, content.written());
......@@ -432,15 +434,15 @@ test "write files" {
432434 }
433435 // without root
434436 {
435 var output: std.Io.Writer.Allocating = .init(testing.allocator);
437 var output: Io.Writer.Allocating = .init(testing.allocator);
436438 var w: Writer = .{ .underlying_writer = &output.writer };
437439 defer output.deinit();
438440 for (files) |file| {
439 var content: std.Io.Reader = .fixed(file.content);
441 var content: Io.Reader = .fixed(file.content);
440442 try w.writeFileStream(file.path, file.content.len, &content, .{});
441443 }
442444
443 var input: std.Io.Reader = .fixed(output.written());
445 var input: Io.Reader = .fixed(output.written());
444446 var it: std.tar.Iterator = .init(&input, .{
445447 .file_name_buffer = &file_name_buffer,
446448 .link_name_buffer = &link_name_buffer,
......@@ -452,7 +454,7 @@ test "write files" {
452454 const expected = files[i];
453455 try testing.expectEqualStrings(expected.path, actual.name);
454456
455 var content: std.Io.Writer.Allocating = .init(testing.allocator);
457 var content: Io.Writer.Allocating = .init(testing.allocator);
456458 defer content.deinit();
457459 try it.streamRemaining(actual, &content.writer);
458460 try testing.expectEqualSlices(u8, expected.content, content.written());
lib/std/zig.zig+1-1
......@@ -559,7 +559,7 @@ test isUnderscore {
559559/// If the source can be UTF-16LE encoded, this function asserts that `gpa`
560560/// will align a byte-sized allocation to at least 2. Allocators that don't do
561561/// this are rare.
562pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *std.fs.File.Reader) ![:0]u8 {
562pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *Io.File.Reader) ![:0]u8 {
563563 var buffer: std.ArrayList(u8) = .empty;
564564 defer buffer.deinit(gpa);
565565
lib/std/zig/system.zig+32-41
......@@ -442,6 +442,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
442442 error.DeviceBusy,
443443 error.InputOutput,
444444 error.LockViolation,
445 error.FileSystem,
445446
446447 error.UnableToOpenElfFile,
447448 error.UnhelpfulFile,
......@@ -542,16 +543,15 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T
542543 return null;
543544}
544545
545pub const AbiAndDynamicLinkerFromFileError = error{};
546
547pub fn abiAndDynamicLinkerFromFile(
546fn abiAndDynamicLinkerFromFile(
548547 file_reader: *Io.File.Reader,
549548 header: *const elf.Header,
550549 cpu: Target.Cpu,
551550 os: Target.Os,
552551 ld_info_list: []const LdInfo,
553552 query: Target.Query,
554) AbiAndDynamicLinkerFromFileError!Target {
553) !Target {
554 const io = file_reader.io;
555555 var result: Target = .{
556556 .cpu = cpu,
557557 .os = os,
......@@ -623,8 +623,8 @@ pub fn abiAndDynamicLinkerFromFile(
623623 try file_reader.seekTo(shstr.sh_offset);
624624 try file_reader.interface.readSliceAll(shstrtab);
625625 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: {
626 var it = header.iterateSectionHeaders(&file_reader.interface);
627 while (it.next()) |shdr| {
626 var it = header.iterateSectionHeaders(file_reader);
627 while (try it.next()) |shdr| {
628628 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
629629 const sh_name = shstrtab[shdr.sh_name..end :0];
630630 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
......@@ -645,7 +645,7 @@ pub fn abiAndDynamicLinkerFromFile(
645645
646646 var it = mem.tokenizeScalar(u8, rpath_list, ':');
647647 while (it.next()) |rpath| {
648 if (glibcVerFromRPath(rpath)) |ver| {
648 if (glibcVerFromRPath(io, rpath)) |ver| {
649649 result.os.version_range.linux.glibc = ver;
650650 return result;
651651 } else |err| switch (err) {
......@@ -660,7 +660,7 @@ pub fn abiAndDynamicLinkerFromFile(
660660 // There is no DT_RUNPATH so we try to find libc.so.6 inside the same
661661 // directory as the dynamic linker.
662662 if (fs.path.dirname(dl_path)) |rpath| {
663 if (glibcVerFromRPath(rpath)) |ver| {
663 if (glibcVerFromRPath(io, rpath)) |ver| {
664664 result.os.version_range.linux.glibc = ver;
665665 return result;
666666 } else |err| switch (err) {
......@@ -725,7 +725,7 @@ pub fn abiAndDynamicLinkerFromFile(
725725 @memcpy(path_buf[index..][0..abi.len], abi);
726726 index += abi.len;
727727 const rpath = path_buf[0..index];
728 if (glibcVerFromRPath(rpath)) |ver| {
728 if (glibcVerFromRPath(io, rpath)) |ver| {
729729 result.os.version_range.linux.glibc = ver;
730730 return result;
731731 } else |err| switch (err) {
......@@ -842,18 +842,13 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
842842 error.InvalidElfMagic,
843843 error.InvalidElfEndian,
844844 error.InvalidElfClass,
845 error.InvalidElfFile,
846845 error.InvalidElfVersion,
847846 error.InvalidGnuLibCVersion,
848847 error.EndOfStream,
849848 => return error.GLibCNotFound,
850849
851 error.SystemResources,
852 error.UnableToReadElfFile,
853 error.Unexpected,
854 error.FileSystem,
855 error.ProcessNotFound,
856 => |e| return e,
850 error.ReadFailed => return file_reader.err.?,
851 else => |e| return e,
857852 };
858853}
859854
......@@ -867,8 +862,8 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
867862 try file_reader.seekTo(shstr.sh_offset);
868863 try file_reader.interface.readSliceAll(shstrtab);
869864 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: {
870 var it = header.iterateSectionHeaders(&file_reader.interface);
871 while (it.next()) |shdr| {
865 var it = header.iterateSectionHeaders(file_reader);
866 while (try it.next()) |shdr| {
872867 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
873868 const sh_name = shstrtab[shdr.sh_name..end :0];
874869 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
......@@ -882,19 +877,25 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
882877 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,
883878 // and furthermore, that the system-installed glibc is at minimum that version.
884879 var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };
885
880 var offset: u64 = 0;
886881 try file_reader.seekTo(dynstr.offset);
887 while (file_reader.interface.takeSentinel(0)) |s| {
888 if (mem.startsWith(u8, s, "GLIBC_2.")) {
889 const chopped = s["GLIBC_".len..];
890 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
891 error.Overflow => return error.InvalidGnuLibCVersion,
892 error.InvalidVersion => return error.InvalidGnuLibCVersion,
893 };
894 switch (ver.order(max_ver)) {
895 .gt => max_ver = ver,
896 .lt, .eq => continue,
882 while (offset < dynstr.size) {
883 if (file_reader.interface.takeSentinel(0)) |s| {
884 if (mem.startsWith(u8, s, "GLIBC_2.")) {
885 const chopped = s["GLIBC_".len..];
886 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
887 error.Overflow => return error.InvalidGnuLibCVersion,
888 error.InvalidVersion => return error.InvalidGnuLibCVersion,
889 };
890 switch (ver.order(max_ver)) {
891 .gt => max_ver = ver,
892 .lt, .eq => continue,
893 }
897894 }
895 offset += s.len + 1;
896 } else |err| switch (err) {
897 error.EndOfStream, error.StreamTooLong => break,
898 error.ReadFailed => |e| return e,
898899 }
899900 }
900901
......@@ -1091,22 +1092,12 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
10911092 error.ProcessFdQuotaExceeded,
10921093 error.SystemFdQuotaExceeded,
10931094 error.ProcessNotFound,
1095 error.Canceled,
10941096 => |e| return e,
10951097
10961098 error.ReadFailed => return file_reader.err.?,
10971099
1098 error.UnableToReadElfFile,
1099 error.InvalidElfClass,
1100 error.InvalidElfVersion,
1101 error.InvalidElfEndian,
1102 error.InvalidElfFile,
1103 error.InvalidElfMagic,
1104 error.Unexpected,
1105 error.EndOfStream,
1106 error.NameTooLong,
1107 error.StaticElfFile,
1108 // Finally, we fall back on the standard path.
1109 => |e| {
1100 else => |e| {
11101101 std.log.warn("encountered {t}; falling back to default ABI and dynamic linker", .{e});
11111102 return defaultAbiAndDynamicLinker(cpu, os, query);
11121103 },
test/src/Cases.zig+2-11
......@@ -455,8 +455,7 @@ pub fn lowerToBuildSteps(
455455 parent_step: *std.Build.Step,
456456 options: CaseTestOptions,
457457) void {
458 const host = std.zig.system.resolveTargetQuery(.{}) catch |err|
459 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});
458 const host = b.resolveTargetQuery(.{});
460459 const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");
461460
462461 for (self.cases.items) |case| {
......@@ -587,7 +586,7 @@ pub fn lowerToBuildSteps(
587586 },
588587 .Execution => |expected_stdout| no_exec: {
589588 const run = if (case.target.result.ofmt == .c) run_step: {
590 if (getExternalExecutor(&host, &case.target.result, .{ .link_libc = true }) != .native) {
589 if (getExternalExecutor(&host.result, &case.target.result, .{ .link_libc = true }) != .native) {
591590 // We wouldn't be able to run the compiled C code.
592591 break :no_exec;
593592 }
......@@ -972,14 +971,6 @@ const TestManifest = struct {
972971 }
973972};
974973
975fn resolveTargetQuery(query: std.Target.Query) std.Build.ResolvedTarget {
976 return .{
977 .query = query,
978 .target = std.zig.system.resolveTargetQuery(query) catch
979 @panic("unable to resolve target query"),
980 };
981}
982
983974fn knownFileExtension(filename: []const u8) bool {
984975 // List taken from `Compilation.classifyFileExt` in the compiler.
985976 for ([_][]const u8{