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 @@...@@ -1,5 +1,8 @@
1const std = @import("std");1const runner = @This();
2const builtin = @import("builtin");2const builtin = @import("builtin");
3
4const std = @import("std");
5const Io = std.Io;
3const assert = std.debug.assert;6const assert = std.debug.assert;
4const fmt = std.fmt;7const fmt = std.fmt;
5const mem = std.mem;8const mem = std.mem;
...@@ -11,7 +14,6 @@ const WebServer = std.Build.WebServer;...@@ -11,7 +14,6 @@ const WebServer = std.Build.WebServer;
11const Allocator = std.mem.Allocator;14const Allocator = std.mem.Allocator;
12const fatal = std.process.fatal;15const fatal = std.process.fatal;
13const Writer = std.Io.Writer;16const Writer = std.Io.Writer;
14const runner = @This();
15const tty = std.Io.tty;17const tty = std.Io.tty;
1618
17pub const root = @import("@build");19pub const root = @import("@build");
...@@ -75,6 +77,7 @@ pub fn main() !void {...@@ -75,6 +77,7 @@ pub fn main() !void {
75 .io = io,77 .io = io,
76 .arena = arena,78 .arena = arena,
77 .cache = .{79 .cache = .{
80 .io = io,
78 .gpa = arena,81 .gpa = arena,
79 .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),82 .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}),
80 },83 },
...@@ -84,7 +87,7 @@ pub fn main() !void {...@@ -84,7 +87,7 @@ pub fn main() !void {
84 .zig_lib_directory = zig_lib_directory,87 .zig_lib_directory = zig_lib_directory,
85 .host = .{88 .host = .{
86 .query = .{},89 .query = .{},
87 .result = try std.zig.system.resolveTargetQuery(.{}),90 .result = try std.zig.system.resolveTargetQuery(io, .{}),
88 },91 },
89 .time_report = false,92 .time_report = false,
90 };93 };
...@@ -121,7 +124,7 @@ pub fn main() !void {...@@ -121,7 +124,7 @@ pub fn main() !void {
121 var watch = false;124 var watch = false;
122 var fuzz: ?std.Build.Fuzz.Mode = null;125 var fuzz: ?std.Build.Fuzz.Mode = null;
123 var debounce_interval_ms: u16 = 50;126 var debounce_interval_ms: u16 = 50;
124 var webui_listen: ?std.net.Address = null;127 var webui_listen: ?Io.net.IpAddress = null;
125128
126 if (try std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(arena)) |str| {129 if (try std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(arena)) |str| {
127 if (std.meta.stringToEnum(ErrorStyle, str)) |style| {130 if (std.meta.stringToEnum(ErrorStyle, str)) |style| {
...@@ -288,11 +291,11 @@ pub fn main() !void {...@@ -288,11 +291,11 @@ pub fn main() !void {
288 });291 });
289 };292 };
290 } else if (mem.eql(u8, arg, "--webui")) {293 } 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) };
292 } else if (mem.startsWith(u8, arg, "--webui=")) {295 } else if (mem.startsWith(u8, arg, "--webui=")) {
293 const addr_str = arg["--webui=".len..];296 const addr_str = arg["--webui=".len..];
294 if (std.mem.eql(u8, addr_str, "-")) fatal("web interface cannot listen on stdio", .{});297 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| {
296 fatal("invalid web UI address '{s}': {s}", .{ addr_str, @errorName(err) });299 fatal("invalid web UI address '{s}': {s}", .{ addr_str, @errorName(err) });
297 };300 };
298 } else if (mem.eql(u8, arg, "--debug-log")) {301 } else if (mem.eql(u8, arg, "--debug-log")) {
...@@ -334,14 +337,10 @@ pub fn main() !void {...@@ -334,14 +337,10 @@ pub fn main() !void {
334 watch = true;337 watch = true;
335 } else if (mem.eql(u8, arg, "--time-report")) {338 } else if (mem.eql(u8, arg, "--time-report")) {
336 graph.time_report = true;339 graph.time_report = true;
337 if (webui_listen == null) {340 if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
338 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
339 }
340 } else if (mem.eql(u8, arg, "--fuzz")) {341 } else if (mem.eql(u8, arg, "--fuzz")) {
341 fuzz = .{ .forever = undefined };342 fuzz = .{ .forever = undefined };
342 if (webui_listen == null) {343 if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) };
343 webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable;
344 }
345 } else if (mem.startsWith(u8, arg, "--fuzz=")) {344 } else if (mem.startsWith(u8, arg, "--fuzz=")) {
346 const value = arg["--fuzz=".len..];345 const value = arg["--fuzz=".len..];
347 if (value.len == 0) fatal("missing argument to --fuzz", .{});346 if (value.len == 0) fatal("missing argument to --fuzz", .{});
...@@ -550,13 +549,15 @@ pub fn main() !void {...@@ -550,13 +549,15 @@ pub fn main() !void {
550549
551 var w: Watch = w: {550 var w: Watch = w: {
552 if (!watch) break :w undefined;551 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});
554 break :w try .init();553 break :w try .init();
555 };554 };
556555
557 try run.thread_pool.init(thread_pool_options);556 try run.thread_pool.init(thread_pool_options);
558 defer run.thread_pool.deinit();557 defer run.thread_pool.deinit();
559558
559 const now = Io.Timestamp.now(io, .awake) catch |err| fatal("failed to collect timestamp: {t}", .{err});
560
560 run.web_server = if (webui_listen) |listen_address| ws: {561 run.web_server = if (webui_listen) |listen_address| ws: {
561 if (builtin.single_threaded) unreachable; // `fatal` above562 if (builtin.single_threaded) unreachable; // `fatal` above
562 break :ws .init(.{563 break :ws .init(.{
...@@ -568,11 +569,12 @@ pub fn main() !void {...@@ -568,11 +569,12 @@ pub fn main() !void {
568 .root_prog_node = main_progress_node,569 .root_prog_node = main_progress_node,
569 .watch = watch,570 .watch = watch,
570 .listen_address = listen_address,571 .listen_address = listen_address,
572 .base_timestamp = now,
571 });573 });
572 } else null;574 } else null;
573575
574 if (run.web_server) |*ws| {576 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});
576 }578 }
577579
578 rebuild: while (true) : (if (run.error_style.clearOnUpdate()) {580 rebuild: while (true) : (if (run.error_style.clearOnUpdate()) {
...@@ -755,6 +757,7 @@ fn runStepNames(...@@ -755,6 +757,7 @@ fn runStepNames(
755 fuzz: ?std.Build.Fuzz.Mode,757 fuzz: ?std.Build.Fuzz.Mode,
756) !void {758) !void {
757 const gpa = run.gpa;759 const gpa = run.gpa;
760 const io = b.graph.io;
758 const step_stack = &run.step_stack;761 const step_stack = &run.step_stack;
759 const thread_pool = &run.thread_pool;762 const thread_pool = &run.thread_pool;
760763
...@@ -858,6 +861,7 @@ fn runStepNames(...@@ -858,6 +861,7 @@ fn runStepNames(
858 assert(mode == .limit);861 assert(mode == .limit);
859 var f = std.Build.Fuzz.init(862 var f = std.Build.Fuzz.init(
860 gpa,863 gpa,
864 io,
861 thread_pool,865 thread_pool,
862 step_stack.keys(),866 step_stack.keys(),
863 parent_prog_node,867 parent_prog_node,
lib/compiler/test_runner.zig+7-8
...@@ -2,6 +2,7 @@...@@ -2,6 +2,7 @@
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4const std = @import("std");4const std = @import("std");
5const Io = std.Io;
5const fatal = std.process.fatal;6const fatal = std.process.fatal;
6const testing = std.testing;7const testing = std.testing;
7const assert = std.debug.assert;8const assert = std.debug.assert;
...@@ -16,6 +17,7 @@ var fba: std.heap.FixedBufferAllocator = .init(&fba_buffer);...@@ -16,6 +17,7 @@ var fba: std.heap.FixedBufferAllocator = .init(&fba_buffer);
16var fba_buffer: [8192]u8 = undefined;17var fba_buffer: [8192]u8 = undefined;
17var stdin_buffer: [4096]u8 = undefined;18var stdin_buffer: [4096]u8 = undefined;
18var stdout_buffer: [4096]u8 = undefined;19var stdout_buffer: [4096]u8 = undefined;
20var runner_threaded_io: Io.Threaded = .init_single_threaded;
1921
20/// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether22/// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether
21/// the test runner will communicate with the build runner via `std.zig.Server`.23/// the test runner will communicate with the build runner via `std.zig.Server`.
...@@ -63,8 +65,6 @@ pub fn main() void {...@@ -63,8 +65,6 @@ pub fn main() void {
63 fuzz_abi.fuzzer_init(.fromSlice(cache_dir));65 fuzz_abi.fuzzer_init(.fromSlice(cache_dir));
64 }66 }
6567
66 fba.reset();
67
68 if (listen) {68 if (listen) {
69 return mainServer() catch @panic("internal test runner failure");69 return mainServer() catch @panic("internal test runner failure");
70 } else {70 } else {
...@@ -74,7 +74,7 @@ pub fn main() void {...@@ -74,7 +74,7 @@ pub fn main() void {
7474
75fn mainServer() !void {75fn mainServer() !void {
76 @disableInstrumentation();76 @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);
78 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);78 var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer);
79 var server = try std.zig.Server.init(.{79 var server = try std.zig.Server.init(.{
80 .in = &stdin_reader.interface,80 .in = &stdin_reader.interface,
...@@ -131,7 +131,7 @@ fn mainServer() !void {...@@ -131,7 +131,7 @@ fn mainServer() !void {
131131
132 .run_test => {132 .run_test => {
133 testing.allocator_instance = .{};133 testing.allocator_instance = .{};
134 testing.io_instance = .init(fba.allocator());134 testing.io_instance = .init(testing.allocator);
135 log_err_count = 0;135 log_err_count = 0;
136 const index = try server.receiveBody_u32();136 const index = try server.receiveBody_u32();
137 const test_fn = builtin.test_functions[index];137 const test_fn = builtin.test_functions[index];
...@@ -154,7 +154,6 @@ fn mainServer() !void {...@@ -154,7 +154,6 @@ fn mainServer() !void {
154 },154 },
155 };155 };
156 testing.io_instance.deinit();156 testing.io_instance.deinit();
157 fba.reset();
158 const leak_count = testing.allocator_instance.detectLeaks();157 const leak_count = testing.allocator_instance.detectLeaks();
159 testing.allocator_instance.deinitWithoutLeakChecks();158 testing.allocator_instance.deinitWithoutLeakChecks();
160 try server.serveTestResults(.{159 try server.serveTestResults(.{
...@@ -234,10 +233,10 @@ fn mainTerminal() void {...@@ -234,10 +233,10 @@ fn mainTerminal() void {
234 var leaks: usize = 0;233 var leaks: usize = 0;
235 for (test_fn_list, 0..) |test_fn, i| {234 for (test_fn_list, 0..) |test_fn, i| {
236 testing.allocator_instance = .{};235 testing.allocator_instance = .{};
237 testing.io_instance = .init(fba.allocator());236 testing.io_instance = .init(testing.allocator);
238 defer {237 defer {
239 if (testing.allocator_instance.deinit() == .leak) leaks += 1;
240 testing.io_instance.deinit();238 testing.io_instance.deinit();
239 if (testing.allocator_instance.deinit() == .leak) leaks += 1;
241 }240 }
242 testing.log_level = .warn;241 testing.log_level = .warn;
243242
...@@ -324,7 +323,7 @@ pub fn mainSimple() anyerror!void {...@@ -324,7 +323,7 @@ pub fn mainSimple() anyerror!void {
324 .stage2_aarch64, .stage2_riscv64 => true,323 .stage2_aarch64, .stage2_riscv64 => true,
325 else => false,324 else => false,
326 };325 };
327 // is the backend capable of calling `std.Io.Writer.print`?326 // is the backend capable of calling `Io.Writer.print`?
328 const enable_print = switch (builtin.zig_backend) {327 const enable_print = switch (builtin.zig_backend) {
329 .stage2_aarch64, .stage2_riscv64 => true,328 .stage2_aarch64, .stage2_riscv64 => true,
330 else => false,329 else => false,
lib/std/Build.zig+3-1
...@@ -1837,6 +1837,8 @@ pub fn runAllowFail(...@@ -1837,6 +1837,8 @@ pub fn runAllowFail(
1837 if (!process.can_spawn)1837 if (!process.can_spawn)
1838 return error.ExecNotSupported;1838 return error.ExecNotSupported;
18391839
1840 const io = b.graph.io;
1841
1840 const max_output_size = 400 * 1024;1842 const max_output_size = 400 * 1024;
1841 var child = std.process.Child.init(argv, b.allocator);1843 var child = std.process.Child.init(argv, b.allocator);
1842 child.stdin_behavior = .Ignore;1844 child.stdin_behavior = .Ignore;
...@@ -1847,7 +1849,7 @@ pub fn runAllowFail(...@@ -1847,7 +1849,7 @@ pub fn runAllowFail(
1847 try Step.handleVerbose2(b, null, child.env_map, argv);1849 try Step.handleVerbose2(b, null, child.env_map, argv);
1848 try child.spawn();1850 try child.spawn();
18491851
1850 var stdout_reader = child.stdout.?.readerStreaming(&.{});1852 var stdout_reader = child.stdout.?.readerStreaming(io, &.{});
1851 const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch {1853 const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch {
1852 return error.ReadFailure;1854 return error.ReadFailure;
1853 };1855 };
lib/std/Build/Cache.zig+16-6
...@@ -3,8 +3,10 @@...@@ -3,8 +3,10 @@
3//! not to withstand attacks using specially-crafted input.3//! not to withstand attacks using specially-crafted input.
44
5const Cache = @This();5const Cache = @This();
6const std = @import("std");
7const builtin = @import("builtin");6const builtin = @import("builtin");
7
8const std = @import("std");
9const Io = std.Io;
8const crypto = std.crypto;10const crypto = std.crypto;
9const fs = std.fs;11const fs = std.fs;
10const assert = std.debug.assert;12const assert = std.debug.assert;
...@@ -15,6 +17,7 @@ const Allocator = std.mem.Allocator;...@@ -15,6 +17,7 @@ const Allocator = std.mem.Allocator;
15const log = std.log.scoped(.cache);17const log = std.log.scoped(.cache);
1618
17gpa: Allocator,19gpa: Allocator,
20io: Io,
18manifest_dir: fs.Dir,21manifest_dir: fs.Dir,
19hash: HashHelper = .{},22hash: HashHelper = .{},
20/// This value is accessed from multiple threads, protected by mutex.23/// This value is accessed from multiple threads, protected by mutex.
...@@ -661,9 +664,10 @@ pub const Manifest = struct {...@@ -661,9 +664,10 @@ pub const Manifest = struct {
661 },664 },
662 } {665 } {
663 const gpa = self.cache.gpa;666 const gpa = self.cache.gpa;
667 const io = self.cache.io;
664 const input_file_count = self.files.entries.len;668 const input_file_count = self.files.entries.len;
665 var tiny_buffer: [1]u8 = undefined; // allows allocRemaining to detect limit exceeded669 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.
667 const limit: std.Io.Limit = .limited(manifest_file_size_max);671 const limit: std.Io.Limit = .limited(manifest_file_size_max);
668 const file_contents = manifest_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) {672 const file_contents = manifest_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) {
669 error.OutOfMemory => return error.OutOfMemory,673 error.OutOfMemory => return error.OutOfMemory,
...@@ -1337,7 +1341,8 @@ test "cache file and then recall it" {...@@ -1337,7 +1341,8 @@ test "cache file and then recall it" {
1337 var digest2: HexDigest = undefined;1341 var digest2: HexDigest = undefined;
13381342
1339 {1343 {
1340 var cache = Cache{1344 var cache: Cache = .{
1345 .io = io,
1341 .gpa = testing.allocator,1346 .gpa = testing.allocator,
1342 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),1347 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
1343 };1348 };
...@@ -1402,7 +1407,8 @@ test "check that changing a file makes cache fail" {...@@ -1402,7 +1407,8 @@ test "check that changing a file makes cache fail" {
1402 var digest2: HexDigest = undefined;1407 var digest2: HexDigest = undefined;
14031408
1404 {1409 {
1405 var cache = Cache{1410 var cache: Cache = .{
1411 .io = io,
1406 .gpa = testing.allocator,1412 .gpa = testing.allocator,
1407 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),1413 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
1408 };1414 };
...@@ -1451,6 +1457,8 @@ test "check that changing a file makes cache fail" {...@@ -1451,6 +1457,8 @@ test "check that changing a file makes cache fail" {
1451}1457}
14521458
1453test "no file inputs" {1459test "no file inputs" {
1460 const io = testing.io;
1461
1454 var tmp = testing.tmpDir(.{});1462 var tmp = testing.tmpDir(.{});
1455 defer tmp.cleanup();1463 defer tmp.cleanup();
14561464
...@@ -1459,7 +1467,8 @@ test "no file inputs" {...@@ -1459,7 +1467,8 @@ test "no file inputs" {
1459 var digest1: HexDigest = undefined;1467 var digest1: HexDigest = undefined;
1460 var digest2: HexDigest = undefined;1468 var digest2: HexDigest = undefined;
14611469
1462 var cache = Cache{1470 var cache: Cache = .{
1471 .io = io,
1463 .gpa = testing.allocator,1472 .gpa = testing.allocator,
1464 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),1473 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
1465 };1474 };
...@@ -1517,7 +1526,8 @@ test "Manifest with files added after initial hash work" {...@@ -1517,7 +1526,8 @@ test "Manifest with files added after initial hash work" {
1517 var digest3: HexDigest = undefined;1526 var digest3: HexDigest = undefined;
15181527
1519 {1528 {
1520 var cache = Cache{1529 var cache: Cache = .{
1530 .io = io,
1521 .gpa = testing.allocator,1531 .gpa = testing.allocator,
1522 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),1532 .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}),
1523 };1533 };
lib/std/Build/Fuzz.zig+6-1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const Io = std.Io;
2const Build = std.Build;3const Build = std.Build;
3const Cache = Build.Cache;4const Cache = Build.Cache;
4const Step = std.Build.Step;5const Step = std.Build.Step;
...@@ -14,6 +15,7 @@ const Fuzz = @This();...@@ -14,6 +15,7 @@ const Fuzz = @This();
14const build_runner = @import("root");15const build_runner = @import("root");
1516
16gpa: Allocator,17gpa: Allocator,
18io: Io,
17mode: Mode,19mode: Mode,
1820
19/// Allocated into `gpa`.21/// Allocated into `gpa`.
...@@ -75,6 +77,7 @@ const CoverageMap = struct {...@@ -75,6 +77,7 @@ const CoverageMap = struct {
7577
76pub fn init(78pub fn init(
77 gpa: Allocator,79 gpa: Allocator,
80 io: Io,
78 thread_pool: *std.Thread.Pool,81 thread_pool: *std.Thread.Pool,
79 all_steps: []const *Build.Step,82 all_steps: []const *Build.Step,
80 root_prog_node: std.Progress.Node,83 root_prog_node: std.Progress.Node,
...@@ -111,6 +114,7 @@ pub fn init(...@@ -111,6 +114,7 @@ pub fn init(
111114
112 return .{115 return .{
113 .gpa = gpa,116 .gpa = gpa,
117 .io = io,
114 .mode = mode,118 .mode = mode,
115 .run_steps = run_steps,119 .run_steps = run_steps,
116 .wait_group = .{},120 .wait_group = .{},
...@@ -484,6 +488,7 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte...@@ -484,6 +488,7 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte
484488
485pub fn waitAndPrintReport(fuzz: *Fuzz) void {489pub fn waitAndPrintReport(fuzz: *Fuzz) void {
486 assert(fuzz.mode == .limit);490 assert(fuzz.mode == .limit);
491 const io = fuzz.io;
487492
488 fuzz.wait_group.wait();493 fuzz.wait_group.wait();
489 fuzz.wait_group.reset();494 fuzz.wait_group.reset();
...@@ -506,7 +511,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {...@@ -506,7 +511,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void {
506511
507 const fuzz_abi = std.Build.abi.fuzz;512 const fuzz_abi = std.Build.abi.fuzz;
508 var rbuf: [0x1000]u8 = undefined;513 var rbuf: [0x1000]u8 = undefined;
509 var r = coverage_file.reader(&rbuf);514 var r = coverage_file.reader(io, &rbuf);
510515
511 var header: fuzz_abi.SeenPcsHeader = undefined;516 var header: fuzz_abi.SeenPcsHeader = undefined;
512 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {517 r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| {
lib/std/Build/Step.zig+11-8
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const Step = @This();1const Step = @This();
2const builtin = @import("builtin");
3
2const std = @import("../std.zig");4const std = @import("../std.zig");
5const Io = std.Io;
3const Build = std.Build;6const Build = std.Build;
4const Allocator = std.mem.Allocator;7const Allocator = std.mem.Allocator;
5const assert = std.debug.assert;8const assert = std.debug.assert;
6const builtin = @import("builtin");
7const Cache = Build.Cache;9const Cache = Build.Cache;
8const Path = Cache.Path;10const Path = Cache.Path;
9const ArrayList = std.ArrayList;11const ArrayList = std.ArrayList;
...@@ -327,7 +329,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {...@@ -327,7 +329,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T {
327}329}
328330
329/// For debugging purposes, prints identifying information about this Step.331/// 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 {
331 if (step.debug_stack_trace.instruction_addresses.len > 0) {333 if (step.debug_stack_trace.instruction_addresses.len > 0) {
332 w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};334 w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {};
333 std.debug.writeStackTrace(&step.debug_stack_trace, w, tty_config) catch {};335 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...@@ -382,7 +384,7 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO
382384
383pub const ZigProcess = struct {385pub const ZigProcess = struct {
384 child: std.process.Child,386 child: std.process.Child,
385 poller: std.Io.Poller(StreamEnum),387 poller: Io.Poller(StreamEnum),
386 progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void,388 progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void,
387389
388 pub const StreamEnum = enum { stdout, stderr };390 pub const StreamEnum = enum { stdout, stderr };
...@@ -458,7 +460,7 @@ pub fn evalZigProcess(...@@ -458,7 +460,7 @@ pub fn evalZigProcess(
458 const zp = try gpa.create(ZigProcess);460 const zp = try gpa.create(ZigProcess);
459 zp.* = .{461 zp.* = .{
460 .child = child,462 .child = child,
461 .poller = std.Io.poll(gpa, ZigProcess.StreamEnum, .{463 .poller = Io.poll(gpa, ZigProcess.StreamEnum, .{
462 .stdout = child.stdout.?,464 .stdout = child.stdout.?,
463 .stderr = child.stderr.?,465 .stderr = child.stderr.?,
464 }),466 }),
...@@ -505,11 +507,12 @@ pub fn evalZigProcess(...@@ -505,11 +507,12 @@ pub fn evalZigProcess(
505}507}
506508
507/// Wrapper around `std.fs.Dir.updateFile` that handles verbose and error output.509/// 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 {
509 const b = s.owner;511 const b = s.owner;
512 const io = b.graph.io;
510 const src_path = src_lazy_path.getPath3(b, s);513 const src_path = src_lazy_path.getPath3(b, s);
511 try handleVerbose(b, null, &.{ "install", "-C", b.fmt("{f}", .{src_path}), dest_path });514 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| {
513 return s.fail("unable to update file from '{f}' to '{s}': {s}", .{516 return s.fail("unable to update file from '{f}' to '{s}': {s}", .{
514 src_path, dest_path, @errorName(err),517 src_path, dest_path, @errorName(err),
515 });518 });
...@@ -738,7 +741,7 @@ pub fn allocPrintCmd2(...@@ -738,7 +741,7 @@ pub fn allocPrintCmd2(
738 argv: []const []const u8,741 argv: []const []const u8,
739) Allocator.Error![]u8 {742) Allocator.Error![]u8 {
740 const shell = struct {743 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 {
742 for (string) |c| {745 for (string) |c| {
743 if (switch (c) {746 if (switch (c) {
744 else => true,747 else => true,
...@@ -772,7 +775,7 @@ pub fn allocPrintCmd2(...@@ -772,7 +775,7 @@ pub fn allocPrintCmd2(
772 }775 }
773 };776 };
774777
775 var aw: std.Io.Writer.Allocating = .init(gpa);778 var aw: Io.Writer.Allocating = .init(gpa);
776 defer aw.deinit();779 defer aw.deinit();
777 const writer = &aw.writer;780 const writer = &aw.writer;
778 if (opt_cwd) |cwd| writer.print("cd {s} && ", .{cwd}) catch return error.OutOfMemory;781 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 {...@@ -538,8 +538,10 @@ test Options {
538 defer arena.deinit();538 defer arena.deinit();
539539
540 var graph: std.Build.Graph = .{540 var graph: std.Build.Graph = .{
541 .io = io,
541 .arena = arena.allocator(),542 .arena = arena.allocator(),
542 .cache = .{543 .cache = .{
544 .io = io,
543 .gpa = arena.allocator(),545 .gpa = arena.allocator(),
544 .manifest_dir = std.fs.cwd(),546 .manifest_dir = std.fs.cwd(),
545 },547 },
lib/std/Build/Step/Run.zig+8-5
...@@ -761,6 +761,7 @@ const IndexedOutput = struct {...@@ -761,6 +761,7 @@ const IndexedOutput = struct {
761};761};
762fn make(step: *Step, options: Step.MakeOptions) !void {762fn make(step: *Step, options: Step.MakeOptions) !void {
763 const b = step.owner;763 const b = step.owner;
764 const io = b.graph.io;
764 const arena = b.allocator;765 const arena = b.allocator;
765 const run: *Run = @fieldParentPtr("step", step);766 const run: *Run = @fieldParentPtr("step", step);
766 const has_side_effects = run.hasSideEffects();767 const has_side_effects = run.hasSideEffects();
...@@ -834,7 +835,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -834,7 +835,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
834 defer file.close();835 defer file.close();
835836
836 var buf: [1024]u8 = undefined;837 var buf: [1024]u8 = undefined;
837 var file_reader = file.reader(&buf);838 var file_reader = file.reader(io, &buf);
838 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {839 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
839 error.ReadFailed => return step.fail(840 error.ReadFailed => return step.fail(
840 "failed to read from '{f}': {t}",841 "failed to read from '{f}': {t}",
...@@ -1067,6 +1068,7 @@ pub fn rerunInFuzzMode(...@@ -1067,6 +1068,7 @@ pub fn rerunInFuzzMode(
1067) !void {1068) !void {
1068 const step = &run.step;1069 const step = &run.step;
1069 const b = step.owner;1070 const b = step.owner;
1071 const io = b.graph.io;
1070 const arena = b.allocator;1072 const arena = b.allocator;
1071 var argv_list: std.ArrayList([]const u8) = .empty;1073 var argv_list: std.ArrayList([]const u8) = .empty;
1072 for (run.argv.items) |arg| {1074 for (run.argv.items) |arg| {
...@@ -1093,7 +1095,7 @@ pub fn rerunInFuzzMode(...@@ -1093,7 +1095,7 @@ pub fn rerunInFuzzMode(
1093 defer file.close();1095 defer file.close();
10941096
1095 var buf: [1024]u8 = undefined;1097 var buf: [1024]u8 = undefined;
1096 var file_reader = file.reader(&buf);1098 var file_reader = file.reader(io, &buf);
1097 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {1099 _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) {
1098 error.ReadFailed => return file_reader.err.?,1100 error.ReadFailed => return file_reader.err.?,
1099 error.WriteFailed => return error.OutOfMemory,1101 error.WriteFailed => return error.OutOfMemory,
...@@ -2090,6 +2092,7 @@ fn sendRunFuzzTestMessage(...@@ -2090,6 +2092,7 @@ fn sendRunFuzzTestMessage(
20902092
2091fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {2093fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
2092 const b = run.step.owner;2094 const b = run.step.owner;
2095 const io = b.graph.io;
2093 const arena = b.allocator;2096 const arena = b.allocator;
20942097
2095 try child.spawn();2098 try child.spawn();
...@@ -2113,7 +2116,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {...@@ -2113,7 +2116,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
2113 defer file.close();2116 defer file.close();
2114 // TODO https://github.com/ziglang/zig/issues/239552117 // TODO https://github.com/ziglang/zig/issues/23955
2115 var read_buffer: [1024]u8 = undefined;2118 var read_buffer: [1024]u8 = undefined;
2116 var file_reader = file.reader(&read_buffer);2119 var file_reader = file.reader(io, &read_buffer);
2117 var write_buffer: [1024]u8 = undefined;2120 var write_buffer: [1024]u8 = undefined;
2118 var stdin_writer = child.stdin.?.writer(&write_buffer);2121 var stdin_writer = child.stdin.?.writer(&write_buffer);
2119 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {2122 _ = stdin_writer.interface.sendFileAll(&file_reader, .unlimited) catch |err| switch (err) {
...@@ -2159,7 +2162,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {...@@ -2159,7 +2162,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
2159 stdout_bytes = try poller.toOwnedSlice(.stdout);2162 stdout_bytes = try poller.toOwnedSlice(.stdout);
2160 stderr_bytes = try poller.toOwnedSlice(.stderr);2163 stderr_bytes = try poller.toOwnedSlice(.stderr);
2161 } else {2164 } else {
2162 var stdout_reader = stdout.readerStreaming(&.{});2165 var stdout_reader = stdout.readerStreaming(io, &.{});
2163 stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {2166 stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
2164 error.OutOfMemory => return error.OutOfMemory,2167 error.OutOfMemory => return error.OutOfMemory,
2165 error.ReadFailed => return stdout_reader.err.?,2168 error.ReadFailed => return stdout_reader.err.?,
...@@ -2167,7 +2170,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {...@@ -2167,7 +2170,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult {
2167 };2170 };
2168 }2171 }
2169 } else if (child.stderr) |stderr| {2172 } else if (child.stderr) |stderr| {
2170 var stderr_reader = stderr.readerStreaming(&.{});2173 var stderr_reader = stderr.readerStreaming(io, &.{});
2171 stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {2174 stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) {
2172 error.OutOfMemory => return error.OutOfMemory,2175 error.OutOfMemory => return error.OutOfMemory,
2173 error.ReadFailed => return stderr_reader.err.?,2176 error.ReadFailed => return stderr_reader.err.?,
lib/std/Build/Step/UpdateSourceFiles.zig+13-11
...@@ -3,11 +3,13 @@...@@ -3,11 +3,13 @@
3//! not be used during the normal build process, but as a utility run by a3//! not be used during the normal build process, but as a utility run by a
4//! developer with intention to update source files, which will then be4//! developer with intention to update source files, which will then be
5//! committed to version control.5//! committed to version control.
6const UpdateSourceFiles = @This();
7
6const std = @import("std");8const std = @import("std");
9const Io = std.Io;
7const Step = std.Build.Step;10const Step = std.Build.Step;
8const fs = std.fs;11const fs = std.fs;
9const ArrayList = std.ArrayList;12const ArrayList = std.ArrayList;
10const UpdateSourceFiles = @This();
1113
12step: Step,14step: Step,
13output_source_files: std.ArrayListUnmanaged(OutputSourceFile),15output_source_files: std.ArrayListUnmanaged(OutputSourceFile),
...@@ -70,22 +72,21 @@ pub fn addBytesToSource(usf: *UpdateSourceFiles, bytes: []const u8, sub_path: []...@@ -70,22 +72,21 @@ pub fn addBytesToSource(usf: *UpdateSourceFiles, bytes: []const u8, sub_path: []
70fn make(step: *Step, options: Step.MakeOptions) !void {72fn make(step: *Step, options: Step.MakeOptions) !void {
71 _ = options;73 _ = options;
72 const b = step.owner;74 const b = step.owner;
75 const io = b.graph.io;
73 const usf: *UpdateSourceFiles = @fieldParentPtr("step", step);76 const usf: *UpdateSourceFiles = @fieldParentPtr("step", step);
7477
75 var any_miss = false;78 var any_miss = false;
76 for (usf.output_source_files.items) |output_source_file| {79 for (usf.output_source_files.items) |output_source_file| {
77 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {80 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
78 b.build_root.handle.makePath(dirname) catch |err| {81 b.build_root.handle.makePath(dirname) catch |err| {
79 return step.fail("unable to make path '{f}{s}': {s}", .{82 return step.fail("unable to make path '{f}{s}': {t}", .{ b.build_root, dirname, err });
80 b.build_root, dirname, @errorName(err),
81 });
82 };83 };
83 }84 }
84 switch (output_source_file.contents) {85 switch (output_source_file.contents) {
85 .bytes => |bytes| {86 .bytes => |bytes| {
86 b.build_root.handle.writeFile(.{ .sub_path = output_source_file.sub_path, .data = bytes }) catch |err| {87 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 return step.fail("unable to write file '{f}{s}': {t}", .{
88 b.build_root, output_source_file.sub_path, @errorName(err),89 b.build_root, output_source_file.sub_path, err,
89 });90 });
90 };91 };
91 any_miss = true;92 any_miss = true;
...@@ -94,15 +95,16 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -94,15 +95,16 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
94 if (!step.inputs.populated()) try step.addWatchInput(file_source);95 if (!step.inputs.populated()) try step.addWatchInput(file_source);
9596
96 const source_path = file_source.getPath2(b, step);97 const source_path = file_source.getPath2(b, step);
97 const prev_status = fs.Dir.updateFile(98 const prev_status = Io.Dir.updateFile(
98 fs.cwd(),99 .cwd(),
100 io,
99 source_path,101 source_path,
100 b.build_root.handle,102 b.build_root.handle.adaptToNewApi(),
101 output_source_file.sub_path,103 output_source_file.sub_path,
102 .{},104 .{},
103 ) catch |err| {105 ) catch |err| {
104 return step.fail("unable to update file from '{s}' to '{f}{s}': {s}", .{106 return step.fail("unable to update file from '{s}' to '{f}{s}': {t}", .{
105 source_path, b.build_root, output_source_file.sub_path, @errorName(err),107 source_path, b.build_root, output_source_file.sub_path, err,
106 });108 });
107 };109 };
108 any_miss = any_miss or prev_status == .stale;110 any_miss = any_miss or prev_status == .stale;
lib/std/Build/Step/WriteFile.zig+13-23
...@@ -2,6 +2,7 @@...@@ -2,6 +2,7 @@
2//! the local cache which has a set of files that have either been generated2//! the local cache which has a set of files that have either been generated
3//! during the build, or are copied from the source package.3//! during the build, or are copied from the source package.
4const std = @import("std");4const std = @import("std");
5const Io = std.Io;
5const Step = std.Build.Step;6const Step = std.Build.Step;
6const fs = std.fs;7const fs = std.fs;
7const ArrayList = std.ArrayList;8const ArrayList = std.ArrayList;
...@@ -174,6 +175,7 @@ fn maybeUpdateName(write_file: *WriteFile) void {...@@ -174,6 +175,7 @@ fn maybeUpdateName(write_file: *WriteFile) void {
174fn make(step: *Step, options: Step.MakeOptions) !void {175fn make(step: *Step, options: Step.MakeOptions) !void {
175 _ = options;176 _ = options;
176 const b = step.owner;177 const b = step.owner;
178 const io = b.graph.io;
177 const arena = b.allocator;179 const arena = b.allocator;
178 const gpa = arena;180 const gpa = arena;
179 const write_file: *WriteFile = @fieldParentPtr("step", step);181 const write_file: *WriteFile = @fieldParentPtr("step", step);
...@@ -264,40 +266,27 @@ fn make(step: *Step, options: Step.MakeOptions) !void {...@@ -264,40 +266,27 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
264 };266 };
265 defer cache_dir.close();267 defer cache_dir.close();
266268
267 const cwd = fs.cwd();
268
269 for (write_file.files.items) |file| {269 for (write_file.files.items) |file| {
270 if (fs.path.dirname(file.sub_path)) |dirname| {270 if (fs.path.dirname(file.sub_path)) |dirname| {
271 cache_dir.makePath(dirname) catch |err| {271 cache_dir.makePath(dirname) catch |err| {
272 return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{272 return step.fail("unable to make path '{f}{s}{c}{s}': {t}", .{
273 b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err),273 b.cache_root, cache_path, fs.path.sep, dirname, err,
274 });274 });
275 };275 };
276 }276 }
277 switch (file.contents) {277 switch (file.contents) {
278 .bytes => |bytes| {278 .bytes => |bytes| {
279 cache_dir.writeFile(.{ .sub_path = file.sub_path, .data = bytes }) catch |err| {279 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}", .{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, @errorName(err),281 b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
282 });282 });
283 };283 };
284 },284 },
285 .copy => |file_source| {285 .copy => |file_source| {
286 const source_path = file_source.getPath2(b, step);286 const source_path = file_source.getPath2(b, step);
287 const prev_status = fs.Dir.updateFile(287 const prev_status = Io.Dir.updateFile(.cwd(), io, source_path, cache_dir.adaptToNewApi(), file.sub_path, .{}) catch |err| {
288 cwd,288 return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {t}", .{
289 source_path,289 source_path, b.cache_root, cache_path, fs.path.sep, file.sub_path, err,
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),
301 });290 });
302 };291 };
303 // At this point we already will mark the step as a cache miss.292 // 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 {...@@ -331,10 +320,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
331 switch (entry.kind) {320 switch (entry.kind) {
332 .directory => try cache_dir.makePath(dest_path),321 .directory => try cache_dir.makePath(dest_path),
333 .file => {322 .file => {
334 const prev_status = fs.Dir.updateFile(323 const prev_status = Io.Dir.updateFile(
335 src_entry_path.root_dir.handle,324 src_entry_path.root_dir.handle.adaptToNewApi(),
325 io,
336 src_entry_path.sub_path,326 src_entry_path.sub_path,
337 cache_dir,327 cache_dir.adaptToNewApi(),
338 dest_path,328 dest_path,
339 .{},329 .{},
340 ) catch |err| {330 ) catch |err| {
lib/std/Build/WebServer.zig+40-27
...@@ -3,14 +3,15 @@ thread_pool: *std.Thread.Pool,...@@ -3,14 +3,15 @@ thread_pool: *std.Thread.Pool,
3graph: *const Build.Graph,3graph: *const Build.Graph,
4all_steps: []const *Build.Step,4all_steps: []const *Build.Step,
5listen_address: net.IpAddress,5listen_address: net.IpAddress,
6ttyconf: std.Io.tty.Config,6ttyconf: Io.tty.Config,
7root_prog_node: std.Progress.Node,7root_prog_node: std.Progress.Node,
8watch: bool,8watch: bool,
99
10tcp_server: ?net.Server,10tcp_server: ?net.Server,
11serve_thread: ?std.Thread,11serve_thread: ?std.Thread,
1212
13base_timestamp: i128,13/// Uses `Io.Clock.awake`.
14base_timestamp: i96,
14/// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`.15/// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`.
15step_names_trailing: []u8,16step_names_trailing: []u8,
1617
...@@ -53,15 +54,17 @@ pub const Options = struct {...@@ -53,15 +54,17 @@ pub const Options = struct {
53 thread_pool: *std.Thread.Pool,54 thread_pool: *std.Thread.Pool,
54 graph: *const std.Build.Graph,55 graph: *const std.Build.Graph,
55 all_steps: []const *Build.Step,56 all_steps: []const *Build.Step,
56 ttyconf: std.Io.tty.Config,57 ttyconf: Io.tty.Config,
57 root_prog_node: std.Progress.Node,58 root_prog_node: std.Progress.Node,
58 watch: bool,59 watch: bool,
59 listen_address: net.IpAddress,60 listen_address: net.IpAddress,
61 base_timestamp: Io.Timestamp,
60};62};
61pub fn init(opts: Options) WebServer {63pub 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`
63 // instead of threads, so that the web server can function in single-threaded builds.65 // instead of threads, so that the web server can function in single-threaded builds.
64 comptime assert(!builtin.single_threaded);66 comptime assert(!builtin.single_threaded);
67 assert(opts.base_timestamp.clock == .awake);
6568
66 const all_steps = opts.all_steps;69 const all_steps = opts.all_steps;
6770
...@@ -106,7 +109,7 @@ pub fn init(opts: Options) WebServer {...@@ -106,7 +109,7 @@ pub fn init(opts: Options) WebServer {
106 .tcp_server = null,109 .tcp_server = null,
107 .serve_thread = null,110 .serve_thread = null,
108111
109 .base_timestamp = std.time.nanoTimestamp(),112 .base_timestamp = opts.base_timestamp.nanoseconds,
110 .step_names_trailing = step_names_trailing,113 .step_names_trailing = step_names_trailing,
111114
112 .step_status_bits = step_status_bits,115 .step_status_bits = step_status_bits,
...@@ -147,32 +150,34 @@ pub fn deinit(ws: *WebServer) void {...@@ -147,32 +150,34 @@ pub fn deinit(ws: *WebServer) void {
147pub fn start(ws: *WebServer) error{AlreadyReported}!void {150pub fn start(ws: *WebServer) error{AlreadyReported}!void {
148 assert(ws.tcp_server == null);151 assert(ws.tcp_server == null);
149 assert(ws.serve_thread == null);152 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| {
152 log.err("failed to listen to port {d}: {s}", .{ ws.listen_address.getPort(), @errorName(err) });156 log.err("failed to listen to port {d}: {s}", .{ ws.listen_address.getPort(), @errorName(err) });
153 return error.AlreadyReported;157 return error.AlreadyReported;
154 };158 };
155 ws.serve_thread = std.Thread.spawn(.{}, serve, .{ws}) catch |err| {159 ws.serve_thread = std.Thread.spawn(.{}, serve, .{ws}) catch |err| {
156 log.err("unable to spawn web server thread: {s}", .{@errorName(err)});160 log.err("unable to spawn web server thread: {s}", .{@errorName(err)});
157 ws.tcp_server.?.deinit();161 ws.tcp_server.?.deinit(io);
158 ws.tcp_server = null;162 ws.tcp_server = null;
159 return error.AlreadyReported;163 return error.AlreadyReported;
160 };164 };
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});
163 if (ws.listen_address.getPort() == 0) {167 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});
165 }169 }
166}170}
167fn serve(ws: *WebServer) void {171fn serve(ws: *WebServer) void {
172 const io = ws.graph.io;
168 while (true) {173 while (true) {
169 const connection = ws.tcp_server.?.accept() catch |err| {174 var stream = ws.tcp_server.?.accept(io) catch |err| {
170 log.err("failed to accept connection: {s}", .{@errorName(err)});175 log.err("failed to accept connection: {s}", .{@errorName(err)});
171 return;176 return;
172 };177 };
173 _ = std.Thread.spawn(.{}, accept, .{ ws, connection }) catch |err| {178 _ = std.Thread.spawn(.{}, accept, .{ ws, stream }) catch |err| {
174 log.err("unable to spawn connection thread: {s}", .{@errorName(err)});179 log.err("unable to spawn connection thread: {s}", .{@errorName(err)});
175 connection.stream.close();180 stream.close(io);
176 continue;181 continue;
177 };182 };
178 }183 }
...@@ -227,6 +232,7 @@ pub fn finishBuild(ws: *WebServer, opts: struct {...@@ -227,6 +232,7 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
227232
228 ws.fuzz = Fuzz.init(233 ws.fuzz = Fuzz.init(
229 ws.gpa,234 ws.gpa,
235 ws.graph.io,
230 ws.thread_pool,236 ws.thread_pool,
231 ws.all_steps,237 ws.all_steps,
232 ws.root_prog_node,238 ws.root_prog_node,
...@@ -241,17 +247,25 @@ pub fn finishBuild(ws: *WebServer, opts: struct {...@@ -241,17 +247,25 @@ pub fn finishBuild(ws: *WebServer, opts: struct {
241}247}
242248
243pub fn now(s: *const WebServer) i64 {249pub 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());
245}254}
246255
247fn accept(ws: *WebServer, connection: net.Server.Connection) void {256fn accept(ws: *WebServer, stream: net.Stream) void {
248 defer connection.stream.close();257 const io = ws.graph.io;
249258 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 }
250 var send_buffer: [4096]u8 = undefined;264 var send_buffer: [4096]u8 = undefined;
251 var recv_buffer: [4096]u8 = undefined;265 var recv_buffer: [4096]u8 = undefined;
252 var connection_reader = connection.stream.reader(&recv_buffer);266 var connection_reader = stream.reader(io, &recv_buffer);
253 var connection_writer = connection.stream.writer(&send_buffer);267 var connection_writer = stream.writer(io, &send_buffer);
254 var server: http.Server = .init(connection_reader.interface(), &connection_writer.interface);268 var server: http.Server = .init(&connection_reader.interface, &connection_writer.interface);
255269
256 while (true) {270 while (true) {
257 var request = server.receiveHead() catch |err| switch (err) {271 var request = server.receiveHead() catch |err| switch (err) {
...@@ -466,12 +480,9 @@ pub fn serveFile(...@@ -466,12 +480,9 @@ pub fn serveFile(
466 },480 },
467 });481 });
468}482}
469pub fn serveTarFile(483pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void {
470 ws: *WebServer,
471 request: *http.Server.Request,
472 paths: []const Cache.Path,
473) !void {
474 const gpa = ws.gpa;484 const gpa = ws.gpa;
485 const io = ws.graph.io;
475486
476 var send_buffer: [0x4000]u8 = undefined;487 var send_buffer: [0x4000]u8 = undefined;
477 var response = try request.respondStreaming(&send_buffer, .{488 var response = try request.respondStreaming(&send_buffer, .{
...@@ -496,7 +507,7 @@ pub fn serveTarFile(...@@ -496,7 +507,7 @@ pub fn serveTarFile(
496 defer file.close();507 defer file.close();
497 const stat = try file.stat();508 const stat = try file.stat();
498 var read_buffer: [1024]u8 = undefined;509 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
501 // TODO: this logic is completely bogus -- obviously so, because `path.root_dir.path` can512 // TODO: this logic is completely bogus -- obviously so, because `path.root_dir.path` can
502 // be cwd-relative. This is also related to why linkification doesn't work in the fuzzer UI:513 // 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...@@ -566,7 +577,7 @@ fn buildClientWasm(ws: *WebServer, arena: Allocator, optimize: std.builtin.Optim
566 child.stderr_behavior = .Pipe;577 child.stderr_behavior = .Pipe;
567 try child.spawn();578 try child.spawn();
568579
569 var poller = std.Io.poll(gpa, enum { stdout, stderr }, .{580 var poller = Io.poll(gpa, enum { stdout, stderr }, .{
570 .stdout = child.stdout.?,581 .stdout = child.stdout.?,
571 .stderr = child.stderr.?,582 .stderr = child.stderr.?,
572 });583 });
...@@ -842,7 +853,10 @@ const cache_control_header: http.Header = .{...@@ -842,7 +853,10 @@ const cache_control_header: http.Header = .{
842};853};
843854
844const builtin = @import("builtin");855const builtin = @import("builtin");
856
845const std = @import("std");857const std = @import("std");
858const Io = std.Io;
859const net = std.Io.net;
846const assert = std.debug.assert;860const assert = std.debug.assert;
847const mem = std.mem;861const mem = std.mem;
848const log = std.log.scoped(.web_server);862const log = std.log.scoped(.web_server);
...@@ -852,6 +866,5 @@ const Cache = Build.Cache;...@@ -852,6 +866,5 @@ const Cache = Build.Cache;
852const Fuzz = Build.Fuzz;866const Fuzz = Build.Fuzz;
853const abi = Build.abi;867const abi = Build.abi;
854const http = std.http;868const http = std.http;
855const net = std.Io.net;
856869
857const WebServer = @This();870const WebServer = @This();
lib/std/Io.zig+12
...@@ -654,6 +654,10 @@ pub const VTable = struct {...@@ -654,6 +654,10 @@ pub const VTable = struct {
654 conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void,654 conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void,
655 conditionWake: *const fn (?*anyopaque, cond: *Condition, wake: Condition.Wake) void,655 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,
657 createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File,661 createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File,
658 fileOpen: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File,662 fileOpen: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File,
659 fileClose: *const fn (?*anyopaque, File) void,663 fileClose: *const fn (?*anyopaque, File) void,
...@@ -804,6 +808,10 @@ pub const Timestamp = struct {...@@ -804,6 +808,10 @@ pub const Timestamp = struct {
804 assert(lhs.clock == rhs.clock);808 assert(lhs.clock == rhs.clock);
805 return std.math.compare(lhs.nanoseconds, op, rhs.nanoseconds);809 return std.math.compare(lhs.nanoseconds, op, rhs.nanoseconds);
806 }810 }
811
812 pub fn toSeconds(t: Timestamp) i64 {
813 return @intCast(@divTrunc(t.nanoseconds, std.time.ns_per_s));
814 }
807};815};
808816
809pub const Duration = struct {817pub const Duration = struct {
...@@ -831,6 +839,10 @@ pub const Duration = struct {...@@ -831,6 +839,10 @@ pub const Duration = struct {
831 return @intCast(@divTrunc(d.nanoseconds, std.time.ns_per_s));839 return @intCast(@divTrunc(d.nanoseconds, std.time.ns_per_s));
832 }840 }
833841
842 pub fn toNanoseconds(d: Duration) i96 {
843 return d.nanoseconds;
844 }
845
834 pub fn sleep(duration: Duration, io: Io) SleepError!void {846 pub fn sleep(duration: Duration, io: Io) SleepError!void {
835 return io.vtable.sleep(io.userdata, .{ .duration = .{ .duration = duration, .clock = .awake } });847 return io.vtable.sleep(io.userdata, .{ .duration = .{ .duration = duration, .clock = .awake } });
836 }848 }
lib/std/Io/Dir.zig+160-5
...@@ -6,6 +6,9 @@ const File = Io.File;...@@ -6,6 +6,9 @@ const File = Io.File;
66
7handle: Handle,7handle: Handle,
88
9pub const Mode = Io.File.Mode;
10pub const default_mode: Mode = 0o755;
11
9pub fn cwd() Dir {12pub fn cwd() Dir {
10 return .{ .handle = std.fs.cwd().fd };13 return .{ .handle = std.fs.cwd().fd };
11}14}
...@@ -47,8 +50,9 @@ pub const UpdateFileError = File.OpenError;...@@ -47,8 +50,9 @@ pub const UpdateFileError = File.OpenError;
4750
48/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If51/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If
49/// they are equal, does nothing. Otherwise, atomically copies `source_path` to52/// they are equal, does nothing. Otherwise, atomically copies `source_path` to
50/// `dest_path`. The destination file gains the mtime, atime, and mode of the53/// `dest_path`, creating the parent directory hierarchy as needed. The
51/// source file so that the next call to `updateFile` will not need a copy.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.
52///56///
53/// Returns the previous status of the file before updating.57/// Returns the previous status of the file before updating.
54///58///
...@@ -65,7 +69,7 @@ pub fn updateFile(...@@ -65,7 +69,7 @@ pub fn updateFile(
65 options: std.fs.Dir.CopyFileOptions,69 options: std.fs.Dir.CopyFileOptions,
66) !PrevStatus {70) !PrevStatus {
67 var src_file = try source_dir.openFile(io, source_path, .{});71 var src_file = try source_dir.openFile(io, source_path, .{});
68 defer src_file.close();72 defer src_file.close(io);
6973
70 const src_stat = try src_file.stat(io);74 const src_stat = try src_file.stat(io);
71 const actual_mode = options.override_mode orelse src_stat.mode;75 const actual_mode = options.override_mode orelse src_stat.mode;
...@@ -93,13 +97,13 @@ pub fn updateFile(...@@ -93,13 +97,13 @@ pub fn updateFile(
93 }97 }
9498
95 var buffer: [1000]u8 = undefined; // Used only when direct fd-to-fd is not available.99 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, .{
97 .mode = actual_mode,101 .mode = actual_mode,
98 .write_buffer = &buffer,102 .write_buffer = &buffer,
99 });103 });
100 defer atomic_file.deinit();104 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);
103 const dest_writer = &atomic_file.file_writer.interface;107 const dest_writer = &atomic_file.file_writer.interface;
104108
105 _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) {109 _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) {
...@@ -111,3 +115,154 @@ pub fn updateFile(...@@ -111,3 +115,154 @@ pub fn updateFile(
111 try atomic_file.renameIntoPlace();115 try atomic_file.renameIntoPlace();
112 return .stale;116 return .stale;
113}117}
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 {...@@ -446,7 +446,11 @@ pub const Reader = struct {
446446
447 fn stream(io_reader: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {447 fn stream(io_reader: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize {
448 const r: *Reader = @alignCast(@fieldParentPtr("interface", io_reader));448 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) {
450 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {454 .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) {
451 error.Unimplemented => {455 error.Unimplemented => {
452 r.mode = r.mode.toReading();456 r.mode = r.mode.toReading();
lib/std/Io/Threaded.zig+69-4
...@@ -63,7 +63,17 @@ const Closure = struct {...@@ -63,7 +63,17 @@ const Closure = struct {
6363
64pub const InitError = std.Thread.CpuCountError || Allocator.Error;64pub 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 {
67 var pool: Pool = .{77 var pool: Pool = .{
68 .allocator = gpa,78 .allocator = gpa,
69 .threads = .empty,79 .threads = .empty,
...@@ -77,6 +87,20 @@ pub fn init(gpa: Allocator) Pool {...@@ -77,6 +87,20 @@ pub fn init(gpa: Allocator) Pool {
77 return pool;87 return pool;
78}88}
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
80pub fn deinit(pool: *Pool) void {104pub fn deinit(pool: *Pool) void {
81 const gpa = pool.allocator;105 const gpa = pool.allocator;
82 pool.join();106 pool.join();
...@@ -136,6 +160,10 @@ pub fn io(pool: *Pool) Io {...@@ -136,6 +160,10 @@ pub fn io(pool: *Pool) Io {
136 .conditionWait = conditionWait,160 .conditionWait = conditionWait,
137 .conditionWake = conditionWake,161 .conditionWake = conditionWake,
138162
163 .dirMake = dirMake,
164 .dirStat = dirStat,
165 .dirStatPath = dirStatPath,
166 .fileStat = fileStat,
139 .createFile = createFile,167 .createFile = createFile,
140 .fileOpen = fileOpen,168 .fileOpen = fileOpen,
141 .fileClose = fileClose,169 .fileClose = fileClose,
...@@ -520,10 +548,11 @@ fn groupAsync(...@@ -520,10 +548,11 @@ fn groupAsync(
520548
521fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {549fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
522 const pool: *Pool = @ptrCast(@alignCast(userdata));550 const pool: *Pool = @ptrCast(@alignCast(userdata));
523 _ = pool;551 const gpa = pool.allocator;
524552
525 if (builtin.single_threaded) return;553 if (builtin.single_threaded) return;
526554
555 // TODO these primitives are too high level, need to check cancel on EINTR
527 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);556 const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
528 const reset_event: *ResetEvent = @ptrCast(&group.context);557 const reset_event: *ResetEvent = @ptrCast(&group.context);
529 std.Thread.WaitGroup.waitStateless(group_state, reset_event);558 std.Thread.WaitGroup.waitStateless(group_state, reset_event);
...@@ -531,8 +560,9 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {...@@ -531,8 +560,9 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
531 var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token));560 var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token));
532 while (true) {561 while (true) {
533 const gc: *GroupClosure = @fieldParentPtr("node", node);562 const gc: *GroupClosure = @fieldParentPtr("node", node);
534 gc.closure.requestCancel();563 const node_next = node.next;
535 node = node.next orelse break;564 gc.free(gpa);
565 node = node_next orelse break;
536 }566 }
537}567}
538568
...@@ -724,6 +754,41 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition....@@ -724,6 +754,41 @@ fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.
724 }754 }
725}755}
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
727fn createFile(792fn createFile(
728 userdata: ?*anyopaque,793 userdata: ?*anyopaque,
729 dir: Io.Dir,794 dir: Io.Dir,
lib/std/Io/Writer.zig+6-2
...@@ -2827,6 +2827,8 @@ pub const Allocating = struct {...@@ -2827,6 +2827,8 @@ pub const Allocating = struct {
2827};2827};
28282828
2829test "discarding sendFile" {2829test "discarding sendFile" {
2830 const io = testing.io;
2831
2830 var tmp_dir = testing.tmpDir(.{});2832 var tmp_dir = testing.tmpDir(.{});
2831 defer tmp_dir.cleanup();2833 defer tmp_dir.cleanup();
28322834
...@@ -2837,7 +2839,7 @@ test "discarding sendFile" {...@@ -2837,7 +2839,7 @@ test "discarding sendFile" {
2837 try file_writer.interface.writeByte('h');2839 try file_writer.interface.writeByte('h');
2838 try file_writer.interface.flush();2840 try file_writer.interface.flush();
28392841
2840 var file_reader = file_writer.moveToReader();2842 var file_reader = file_writer.moveToReader(io);
2841 try file_reader.seekTo(0);2843 try file_reader.seekTo(0);
28422844
2843 var w_buffer: [256]u8 = undefined;2845 var w_buffer: [256]u8 = undefined;
...@@ -2847,6 +2849,8 @@ test "discarding sendFile" {...@@ -2847,6 +2849,8 @@ test "discarding sendFile" {
2847}2849}
28482850
2849test "allocating sendFile" {2851test "allocating sendFile" {
2852 const io = testing.io;
2853
2850 var tmp_dir = testing.tmpDir(.{});2854 var tmp_dir = testing.tmpDir(.{});
2851 defer tmp_dir.cleanup();2855 defer tmp_dir.cleanup();
28522856
...@@ -2857,7 +2861,7 @@ test "allocating sendFile" {...@@ -2857,7 +2861,7 @@ test "allocating sendFile" {
2857 try file_writer.interface.writeAll("abcd");2861 try file_writer.interface.writeAll("abcd");
2858 try file_writer.interface.flush();2862 try file_writer.interface.flush();
28592863
2860 var file_reader = file_writer.moveToReader();2864 var file_reader = file_writer.moveToReader(io);
2861 try file_reader.seekTo(0);2865 try file_reader.seekTo(0);
2862 try file_reader.interface.fill(2);2866 try file_reader.interface.fill(2);
28632867
lib/std/Io/net.zig+33
...@@ -57,6 +57,39 @@ pub const IpAddress = union(enum) {...@@ -57,6 +57,39 @@ pub const IpAddress = union(enum) {
5757
58 pub const Family = @typeInfo(IpAddress).@"union".tag_type.?;58 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
60 /// Parse the given IP address string into an `IpAddress` value.93 /// Parse the given IP address string into an `IpAddress` value.
61 ///94 ///
62 /// This is a pure function but it cannot handle IPv6 addresses that have95 /// 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{...@@ -77,7 +77,9 @@ pub const LookupError = error{
77 InvalidDnsAAAARecord,77 InvalidDnsAAAARecord,
78 InvalidDnsCnameRecord,78 InvalidDnsCnameRecord,
79 NameServerFailure,79 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
82pub const LookupResult = struct {84pub const LookupResult = struct {
83 /// How many `LookupOptions.addresses_buffer` elements are populated.85 /// How many `LookupOptions.addresses_buffer` elements are populated.
...@@ -428,14 +430,25 @@ fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResul...@@ -428,14 +430,25 @@ fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResul
428 error.AccessDenied,430 error.AccessDenied,
429 => return .empty,431 => 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 },
432 };439 };
433 defer file.close(io);440 defer file.close(io);
434441
435 var line_buf: [512]u8 = undefined;442 var line_buf: [512]u8 = undefined;
436 var file_reader = file.reader(io, &line_buf);443 var file_reader = file.reader(io, &line_buf);
437 return lookupHostsReader(host_name, options, &file_reader.interface) catch |err| switch (err) {444 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 },
439 };452 };
440}453}
441454
lib/std/Io/net/test.zig+15-16
...@@ -211,11 +211,11 @@ test "listen on a port, send bytes, receive bytes" {...@@ -211,11 +211,11 @@ test "listen on a port, send bytes, receive bytes" {
211 const t = try std.Thread.spawn(.{}, S.clientFn, .{server.socket.address});211 const t = try std.Thread.spawn(.{}, S.clientFn, .{server.socket.address});
212 defer t.join();212 defer t.join();
213213
214 var client = try server.accept(io);214 var stream = try server.accept(io);
215 defer client.stream.close(io);215 defer stream.close(io);
216 var buf: [16]u8 = undefined;216 var buf: [16]u8 = undefined;
217 var stream_reader = client.stream.reader(io, &.{});217 var stream_reader = stream.reader(io, &.{});
218 const n = try stream_reader.interface().readSliceShort(&buf);218 const n = try stream_reader.interface.readSliceShort(&buf);
219219
220 try testing.expectEqual(@as(usize, 12), n);220 try testing.expectEqual(@as(usize, 12), n);
221 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);221 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
...@@ -267,10 +267,9 @@ fn testServer(server: *net.Server) anyerror!void {...@@ -267,10 +267,9 @@ fn testServer(server: *net.Server) anyerror!void {
267267
268 const io = testing.io;268 const io = testing.io;
269269
270 var client = try server.accept(io);270 var stream = try server.accept(io);
271271 var writer = stream.writer(io, &.{});
272 const stream = client.stream.writer(io);272 try writer.interface.print("hello from server\n", .{});
273 try stream.print("hello from server\n", .{});
274}273}
275274
276test "listen on a unix socket, send bytes, receive bytes" {275test "listen on a unix socket, send bytes, receive bytes" {
...@@ -310,11 +309,11 @@ test "listen on a unix socket, send bytes, receive bytes" {...@@ -310,11 +309,11 @@ test "listen on a unix socket, send bytes, receive bytes" {
310 const t = try std.Thread.spawn(.{}, S.clientFn, .{socket_path});309 const t = try std.Thread.spawn(.{}, S.clientFn, .{socket_path});
311 defer t.join();310 defer t.join();
312311
313 var client = try server.accept(io);312 var stream = try server.accept(io);
314 defer client.stream.close(io);313 defer stream.close(io);
315 var buf: [16]u8 = undefined;314 var buf: [16]u8 = undefined;
316 var stream_reader = client.stream.reader(io, &.{});315 var stream_reader = stream.reader(io, &.{});
317 const n = try stream_reader.interface().readSliceShort(&buf);316 const n = try stream_reader.interface.readSliceShort(&buf);
318317
319 try testing.expectEqual(@as(usize, 12), n);318 try testing.expectEqual(@as(usize, 12), n);
320 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);319 try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]);
...@@ -366,10 +365,10 @@ test "non-blocking tcp server" {...@@ -366,10 +365,10 @@ test "non-blocking tcp server" {
366 const socket_file = try net.tcpConnectToAddress(server.socket.address);365 const socket_file = try net.tcpConnectToAddress(server.socket.address);
367 defer socket_file.close();366 defer socket_file.close();
368367
369 var client = try server.accept(io);368 var stream = try server.accept(io);
370 defer client.stream.close(io);369 defer stream.close(io);
371 const stream = client.stream.writer(io);370 var writer = stream.writer(io, .{});
372 try stream.print("hello from server\n", .{});371 try writer.interface.print("hello from server\n", .{});
373372
374 var buf: [100]u8 = undefined;373 var buf: [100]u8 = undefined;
375 const len = try socket_file.read(&buf);374 const len = try socket_file.read(&buf);
lib/std/crypto/tls/Client.zig+12-8
...@@ -105,6 +105,14 @@ pub const Options = struct {...@@ -105,6 +105,14 @@ pub const Options = struct {
105 /// Verify that the server certificate is authorized by a given ca bundle.105 /// Verify that the server certificate is authorized by a given ca bundle.
106 bundle: Certificate.Bundle,106 bundle: Certificate.Bundle,
107 },107 },
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
108 /// If non-null, ssl secrets are logged to this stream. Creating such a log file allows116 /// If non-null, ssl secrets are logged to this stream. Creating such a log file allows
109 /// other programs with access to that file to decrypt all traffic over this connection.117 /// other programs with access to that file to decrypt all traffic over this connection.
110 ///118 ///
...@@ -120,8 +128,6 @@ pub const Options = struct {...@@ -120,8 +128,6 @@ pub const Options = struct {
120 /// application layer itself verifies that the amount of data received equals128 /// application layer itself verifies that the amount of data received equals
121 /// the amount of data expected, such as HTTP with the Content-Length header.129 /// the amount of data expected, such as HTTP with the Content-Length header.
122 allow_truncation_attacks: bool = false,130 allow_truncation_attacks: bool = false,
123 write_buffer: []u8,
124 read_buffer: []u8,
125 /// Populated when `error.TlsAlert` is returned from `init`.131 /// Populated when `error.TlsAlert` is returned from `init`.
126 alert: ?*tls.Alert = null,132 alert: ?*tls.Alert = null,
127};133};
...@@ -189,14 +195,12 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client...@@ -189,14 +195,12 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
189 };195 };
190 const host_len: u16 = @intCast(host.len);196 const host_len: u16 = @intCast(host.len);
191197
192 var random_buffer: [176]u8 = undefined;198 const client_hello_rand = options.entropy[0..32].*;
193 crypto.random.bytes(&random_buffer);
194 const client_hello_rand = random_buffer[0..32].*;
195 var key_seq: u64 = 0;199 var key_seq: u64 = 0;
196 var server_hello_rand: [32]u8 = undefined;200 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) {
200 // Only possible to happen if the seed is all zeroes.204 // Only possible to happen if the seed is all zeroes.
201 error.IdentityElement => return error.InsufficientEntropy,205 error.IdentityElement => return error.InsufficientEntropy,
202 };206 };
...@@ -321,7 +325,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client...@@ -321,7 +325,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client
321 var handshake_cipher: tls.HandshakeCipher = undefined;325 var handshake_cipher: tls.HandshakeCipher = undefined;
322 var main_cert_pub_key: CertificatePublicKey = undefined;326 var main_cert_pub_key: CertificatePublicKey = undefined;
323 var tls12_negotiated_group: ?tls.NamedGroup = null;327 var tls12_negotiated_group: ?tls.NamedGroup = null;
324 const now_sec = std.time.timestamp();328 const now_sec = options.realtime_now_seconds;
325329
326 var cleartext_fragment_start: usize = 0;330 var cleartext_fragment_start: usize = 0;
327 var cleartext_fragment_end: usize = 0;331 var cleartext_fragment_end: usize = 0;
lib/std/debug/SelfInfo/Windows.zig+2-1
...@@ -434,7 +434,7 @@ const Module = struct {...@@ -434,7 +434,7 @@ const Module = struct {
434 };434 };
435 errdefer pdb_file.close();435 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);
438 pdb_reader.* = pdb_file.reader(try arena.alloc(u8, 4096));438 pdb_reader.* = pdb_file.reader(try arena.alloc(u8, 4096));
439439
440 var pdb = Pdb.init(gpa, pdb_reader) catch |err| switch (err) {440 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...@@ -544,6 +544,7 @@ fn findModule(si: *SelfInfo, gpa: Allocator, address: usize) error{ MissingDebug
544}544}
545545
546const std = @import("std");546const std = @import("std");
547const Io = std.Io;
547const Allocator = std.mem.Allocator;548const Allocator = std.mem.Allocator;
548const Dwarf = std.debug.Dwarf;549const Dwarf = std.debug.Dwarf;
549const Pdb = std.debug.Pdb;550const Pdb = std.debug.Pdb;
lib/std/elf.zig+6-6
...@@ -710,7 +710,7 @@ pub const ProgramHeaderIterator = struct {...@@ -710,7 +710,7 @@ pub const ProgramHeaderIterator = struct {
710 const offset = it.phoff + size * it.index;710 const offset = it.phoff + size * it.index;
711 try it.file_reader.seekTo(offset);711 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);
714 }714 }
715};715};
716716
...@@ -731,7 +731,7 @@ pub const ProgramHeaderBufferIterator = struct {...@@ -731,7 +731,7 @@ pub const ProgramHeaderBufferIterator = struct {
731 const offset = it.phoff + size * it.index;731 const offset = it.phoff + size * it.index;
732 var reader = Io.Reader.fixed(it.buf[offset..]);732 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);
735 }735 }
736};736};
737737
...@@ -771,7 +771,7 @@ pub const SectionHeaderIterator = struct {...@@ -771,7 +771,7 @@ pub const SectionHeaderIterator = struct {
771 const offset = it.shoff + size * it.index;771 const offset = it.shoff + size * it.index;
772 try it.file_reader.seekTo(offset);772 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);
775 }775 }
776};776};
777777
...@@ -793,7 +793,7 @@ pub const SectionHeaderBufferIterator = struct {...@@ -793,7 +793,7 @@ pub const SectionHeaderBufferIterator = struct {
793 if (offset > it.buf.len) return error.EndOfStream;793 if (offset > it.buf.len) return error.EndOfStream;
794 var reader = Io.Reader.fixed(it.buf[@intCast(offset)..]);794 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);
797 }797 }
798};798};
799799
...@@ -826,12 +826,12 @@ pub const DynamicSectionIterator = struct {...@@ -826,12 +826,12 @@ pub const DynamicSectionIterator = struct {
826826
827 file_reader: *Io.File.Reader,827 file_reader: *Io.File.Reader,
828828
829 pub fn next(it: *SectionHeaderIterator) !?Elf64_Dyn {829 pub fn next(it: *DynamicSectionIterator) !?Elf64_Dyn {
830 if (it.offset >= it.end_offset) return null;830 if (it.offset >= it.end_offset) return null;
831 const size: u64 = if (it.is_64) @sizeOf(Elf64_Dyn) else @sizeOf(Elf32_Dyn);831 const size: u64 = if (it.is_64) @sizeOf(Elf64_Dyn) else @sizeOf(Elf32_Dyn);
832 defer it.offset += size;832 defer it.offset += size;
833 try it.file_reader.seekTo(it.offset);833 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);
835 }835 }
836};836};
837837
lib/std/fs/Dir.zig+56-94
...@@ -1,6 +1,11 @@...@@ -1,6 +1,11 @@
1//! Deprecated in favor of `Io.Dir`.
1const Dir = @This();2const Dir = @This();
3
2const builtin = @import("builtin");4const builtin = @import("builtin");
5const native_os = builtin.os.tag;
6
3const std = @import("../std.zig");7const std = @import("../std.zig");
8const Io = std.Io;
4const File = std.fs.File;9const File = std.fs.File;
5const AtomicFile = std.fs.AtomicFile;10const AtomicFile = std.fs.AtomicFile;
6const base64_encoder = fs.base64_encoder;11const base64_encoder = fs.base64_encoder;
...@@ -12,7 +17,6 @@ const Allocator = std.mem.Allocator;...@@ -12,7 +17,6 @@ const Allocator = std.mem.Allocator;
12const assert = std.debug.assert;17const assert = std.debug.assert;
13const linux = std.os.linux;18const linux = std.os.linux;
14const windows = std.os.windows;19const windows = std.os.windows;
15const native_os = builtin.os.tag;
16const have_flock = @TypeOf(posix.system.flock) != void;20const have_flock = @TypeOf(posix.system.flock) != void;
1721
18fd: Handle,22fd: Handle,
...@@ -1189,84 +1193,41 @@ pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags)...@@ -1189,84 +1193,41 @@ pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags)
1189 return file;1193 return file;
1190}1194}
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.1199/// Deprecated in favor of `Io.Dir.makeDir`.
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.
1200pub fn makeDir(self: Dir, sub_path: []const u8) MakeError!void {1200pub 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);
1202}1204}
12031205
1204/// Same as `makeDir`, but `sub_path` is null-terminated.1206/// Deprecated in favor of `Io.Dir.makeDir`.
1205/// To create multiple directories to make an entire path, see `makePath`.
1206/// To operate on only absolute paths, see `makeDirAbsoluteZ`.
1207pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) MakeError!void {1207pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) MakeError!void {
1208 try posix.mkdiratZ(self.fd, sub_path, default_mode);1208 try posix.mkdiratZ(self.fd, sub_path, default_mode);
1209}1209}
12101210
1211/// Creates a single directory with a relative or absolute null-terminated WTF-16 LE-encoded path.1211/// Deprecated in favor of `Io.Dir.makeDir`.
1212/// To create multiple directories to make an entire path, see `makePath`.
1213/// To operate on only absolute paths, see `makeDirAbsoluteW`.
1214pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {1212pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void {
1215 try posix.mkdiratW(self.fd, mem.span(sub_path), default_mode);1213 try posix.mkdiratW(self.fd, mem.span(sub_path), default_mode);
1216}1214}
12171215
1218/// Calls makeDir iteratively to make an entire path1216/// Deprecated in favor of `Io.Dir.makePath`.
1219/// (i.e. creating any parent directories that do not exist).1217pub fn makePath(self: Dir, sub_path: []const u8) MakePathError!void {
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 {
1236 _ = try self.makePathStatus(sub_path);1218 _ = try self.makePathStatus(sub_path);
1237}1219}
12381220
1239pub const MakePathStatus = enum { existed, created };1221/// Deprecated in favor of `Io.Dir.MakePathStatus`.
1240/// Same as `makePath` except returns whether the path already existed or was successfully created.1222pub const MakePathStatus = Io.Dir.MakePathStatus;
1241pub fn makePathStatus(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!MakePathStatus {1223/// Deprecated in favor of `Io.Dir.MakePathError`.
1242 var it = try fs.path.componentIterator(sub_path);1224pub const MakePathError = Io.Dir.MakePathError;
1243 var status: MakePathStatus = .existed;1225
1244 var component = it.last() orelse return error.BadPathName;1226/// Deprecated in favor of `Io.Dir.makePathStatus`.
1245 while (true) {1227pub fn makePathStatus(self: Dir, sub_path: []const u8) MakePathError!MakePathStatus {
1246 if (self.makeDir(component.path)) |_| {1228 var threaded: Io.Threaded = .init_single_threaded;
1247 status = .created;1229 const io = threaded.io();
1248 } else |err| switch (err) {1230 return Io.Dir.makePathStatus(.{ .handle = self.fd }, io, sub_path);
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 }
1270}1231}
12711232
1272/// Windows only. Calls makeOpenDirAccessMaskW iteratively to make an entire path1233/// 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 {...@@ -2052,20 +2013,11 @@ pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
2052 return windows.ReadLink(self.fd, sub_path_w, buffer);2013 return windows.ReadLink(self.fd, sub_path_w, buffer);
2053}2014}
20542015
2055/// Read all of file contents using a preallocated buffer.2016/// Deprecated in favor of `Io.Dir.readFile`.
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.
2063pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 {2017pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 {
2064 var file = try self.openFile(file_path, .{});2018 var threaded: Io.Threaded = .init_single_threaded;
2065 defer file.close();2019 const io = threaded.io();
20662020 return Io.Dir.readFile(.{ .handle = self.fd }, io, file_path, buffer);
2067 const end_index = try file.readAll(buffer);
2068 return buffer[0..end_index];
2069}2021}
20702022
2071pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{2023pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{
...@@ -2091,7 +2043,7 @@ pub fn readFileAlloc(...@@ -2091,7 +2043,7 @@ pub fn readFileAlloc(
2091 /// Used to allocate the result.2043 /// Used to allocate the result.
2092 gpa: Allocator,2044 gpa: Allocator,
2093 /// If reached or exceeded, `error.StreamTooLong` is returned instead.2045 /// If reached or exceeded, `error.StreamTooLong` is returned instead.
2094 limit: std.Io.Limit,2046 limit: Io.Limit,
2095) ReadFileAllocError![]u8 {2047) ReadFileAllocError![]u8 {
2096 return readFileAllocOptions(dir, sub_path, gpa, limit, .of(u8), null);2048 return readFileAllocOptions(dir, sub_path, gpa, limit, .of(u8), null);
2097}2049}
...@@ -2101,6 +2053,8 @@ pub fn readFileAlloc(...@@ -2101,6 +2053,8 @@ pub fn readFileAlloc(
2101///2053///
2102/// If the file size is already known, a better alternative is to initialize a2054/// If the file size is already known, a better alternative is to initialize a
2103/// `File.Reader`.2055/// `File.Reader`.
2056///
2057/// TODO move this function to Io.Dir
2104pub fn readFileAllocOptions(2058pub fn readFileAllocOptions(
2105 dir: Dir,2059 dir: Dir,
2106 /// On Windows, should be encoded as [WTF-8](https://wtf-8.codeberg.page/).2060 /// On Windows, should be encoded as [WTF-8](https://wtf-8.codeberg.page/).
...@@ -2110,13 +2064,16 @@ pub fn readFileAllocOptions(...@@ -2110,13 +2064,16 @@ pub fn readFileAllocOptions(
2110 /// Used to allocate the result.2064 /// Used to allocate the result.
2111 gpa: Allocator,2065 gpa: Allocator,
2112 /// If reached or exceeded, `error.StreamTooLong` is returned instead.2066 /// If reached or exceeded, `error.StreamTooLong` is returned instead.
2113 limit: std.Io.Limit,2067 limit: Io.Limit,
2114 comptime alignment: std.mem.Alignment,2068 comptime alignment: std.mem.Alignment,
2115 comptime sentinel: ?u8,2069 comptime sentinel: ?u8,
2116) ReadFileAllocError!(if (sentinel) |s| [:s]align(alignment.toByteUnits()) u8 else []align(alignment.toByteUnits()) u8) {2070) 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
2117 var file = try dir.openFile(sub_path, .{});2074 var file = try dir.openFile(sub_path, .{});
2118 defer file.close();2075 defer file.close();
2119 var file_reader = file.reader(&.{});2076 var file_reader = file.reader(io, &.{});
2120 return file_reader.interface.allocRemainingAlignedSentinel(gpa, limit, alignment, sentinel) catch |err| switch (err) {2077 return file_reader.interface.allocRemainingAlignedSentinel(gpa, limit, alignment, sentinel) catch |err| switch (err) {
2121 error.ReadFailed => return file_reader.err.?,2078 error.ReadFailed => return file_reader.err.?,
2122 error.OutOfMemory, error.StreamTooLong => |e| return e,2079 error.OutOfMemory, error.StreamTooLong => |e| return e,
...@@ -2647,6 +2604,8 @@ pub const CopyFileError = File.OpenError || File.StatError ||...@@ -2647,6 +2604,8 @@ pub const CopyFileError = File.OpenError || File.StatError ||
2647/// [WTF-8](https://wtf-8.codeberg.page/). On WASI, both paths should be2604/// [WTF-8](https://wtf-8.codeberg.page/). On WASI, both paths should be
2648/// encoded as valid UTF-8. On other platforms, both paths are an opaque2605/// encoded as valid UTF-8. On other platforms, both paths are an opaque
2649/// sequence of bytes with no particular encoding.2606/// sequence of bytes with no particular encoding.
2607///
2608/// TODO move this function to Io.Dir
2650pub fn copyFile(2609pub fn copyFile(
2651 source_dir: Dir,2610 source_dir: Dir,
2652 source_path: []const u8,2611 source_path: []const u8,
...@@ -2654,11 +2613,15 @@ pub fn copyFile(...@@ -2654,11 +2613,15 @@ pub fn copyFile(
2654 dest_path: []const u8,2613 dest_path: []const u8,
2655 options: CopyFileOptions,2614 options: CopyFileOptions,
2656) CopyFileError!void {2615) CopyFileError!void {
2657 var file_reader: File.Reader = .init(try source_dir.openFile(source_path, .{}), &.{});2616 var threaded: Io.Threaded = .init_single_threaded;
2658 defer file_reader.file.close();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
2660 const mode = options.override_mode orelse blk: {2623 const mode = options.override_mode orelse blk: {
2661 const st = try file_reader.file.stat();2624 const st = try file_reader.file.stat(io);
2662 file_reader.size = st.size;2625 file_reader.size = st.size;
2663 break :blk st.mode;2626 break :blk st.mode;
2664 };2627 };
...@@ -2708,6 +2671,7 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions)...@@ -2708,6 +2671,7 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions)
2708pub const Stat = File.Stat;2671pub const Stat = File.Stat;
2709pub const StatError = File.StatError;2672pub const StatError = File.StatError;
27102673
2674/// Deprecated in favor of `Io.Dir.stat`.
2711pub fn stat(self: Dir) StatError!Stat {2675pub fn stat(self: Dir) StatError!Stat {
2712 const file: File = .{ .handle = self.fd };2676 const file: File = .{ .handle = self.fd };
2713 return file.stat();2677 return file.stat();
...@@ -2715,17 +2679,7 @@ pub fn stat(self: Dir) StatError!Stat {...@@ -2715,17 +2679,7 @@ pub fn stat(self: Dir) StatError!Stat {
27152679
2716pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError;2680pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError;
27172681
2718/// Returns metadata for a file inside the directory.2682/// Deprecated in favor of `Io.Dir.statPath`.
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.
2729pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {2683pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
2730 if (native_os == .windows) {2684 if (native_os == .windows) {
2731 var file = try self.openFile(sub_path, .{});2685 var file = try self.openFile(sub_path, .{});
...@@ -2799,3 +2753,11 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v...@@ -2799,3 +2753,11 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v
2799 const file: File = .{ .handle = self.fd };2753 const file: File = .{ .handle = self.fd };
2800 try file.setPermissions(permissions);2754 try file.setPermissions(permissions);
2801}2755}
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 {...@@ -858,10 +858,12 @@ pub const Writer = struct {
858 };858 };
859 }859 }
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 {
862 defer w.* = undefined;863 defer w.* = undefined;
863 return .{864 return .{
864 .file = w.file,865 .io = io,
866 .file = .{ .handle = w.file.handle },
865 .mode = w.mode,867 .mode = w.mode,
866 .pos = w.pos,868 .pos = w.pos,
867 .interface = Reader.initInterface(w.interface.buffer),869 .interface = Reader.initInterface(w.interface.buffer),
...@@ -1350,15 +1352,15 @@ pub const Writer = struct {...@@ -1350,15 +1352,15 @@ pub const Writer = struct {
1350///1352///
1351/// Positional is more threadsafe, since the global seek position is not1353/// Positional is more threadsafe, since the global seek position is not
1352/// affected.1354/// affected.
1353pub fn reader(file: File, buffer: []u8) Reader {1355pub fn reader(file: File, io: std.Io, buffer: []u8) Reader {
1354 return .init(file, buffer);1356 return .init(.{ .handle = file.handle }, io, buffer);
1355}1357}
13561358
1357/// Positional is more threadsafe, since the global seek position is not1359/// Positional is more threadsafe, since the global seek position is not
1358/// affected, but when such syscalls are not available, preemptively1360/// affected, but when such syscalls are not available, preemptively
1359/// initializing in streaming mode skips a failed syscall.1361/// initializing in streaming mode skips a failed syscall.
1360pub fn readerStreaming(file: File, buffer: []u8) Reader {1362pub fn readerStreaming(file: File, io: std.Io, buffer: []u8) Reader {
1361 return .initStreaming(file, buffer);1363 return .initStreaming(.{ .handle = file.handle }, io, buffer);
1362}1364}
13631365
1364/// Defaults to positional reading; falls back to streaming.1366/// Defaults to positional reading; falls back to streaming.
...@@ -1538,3 +1540,11 @@ pub fn downgradeLock(file: File) LockError!void {...@@ -1538,3 +1540,11 @@ pub fn downgradeLock(file: File) LockError!void {
1538 };1540 };
1539 }1541 }
1540}1542}
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 @@...@@ -1,10 +1,12 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");1const builtin = @import("builtin");
2const native_os = builtin.os.tag;
3
4const std = @import("../std.zig");
5const Io = std.Io;
3const testing = std.testing;6const testing = std.testing;
4const fs = std.fs;7const fs = std.fs;
5const mem = std.mem;8const mem = std.mem;
6const wasi = std.os.wasi;9const wasi = std.os.wasi;
7const native_os = builtin.os.tag;
8const windows = std.os.windows;10const windows = std.os.windows;
9const posix = std.posix;11const posix = std.posix;
1012
...@@ -73,6 +75,7 @@ const PathType = enum {...@@ -73,6 +75,7 @@ const PathType = enum {
73};75};
7476
75const TestContext = struct {77const TestContext = struct {
78 io: Io,
76 path_type: PathType,79 path_type: PathType,
77 path_sep: u8,80 path_sep: u8,
78 arena: ArenaAllocator,81 arena: ArenaAllocator,
...@@ -83,6 +86,7 @@ const TestContext = struct {...@@ -83,6 +86,7 @@ const TestContext = struct {
83 pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {86 pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {
84 const tmp = tmpDir(.{ .iterate = true });87 const tmp = tmpDir(.{ .iterate = true });
85 return .{88 return .{
89 .io = testing.io,
86 .path_type = path_type,90 .path_type = path_type,
87 .path_sep = path_sep,91 .path_sep = path_sep,
88 .arena = ArenaAllocator.init(allocator),92 .arena = ArenaAllocator.init(allocator),
...@@ -1319,6 +1323,8 @@ test "max file name component lengths" {...@@ -1319,6 +1323,8 @@ test "max file name component lengths" {
1319}1323}
13201324
1321test "writev, readv" {1325test "writev, readv" {
1326 const io = testing.io;
1327
1322 var tmp = tmpDir(.{});1328 var tmp = tmpDir(.{});
1323 defer tmp.cleanup();1329 defer tmp.cleanup();
13241330
...@@ -1327,78 +1333,55 @@ test "writev, readv" {...@@ -1327,78 +1333,55 @@ test "writev, readv" {
13271333
1328 var buf1: [line1.len]u8 = undefined;1334 var buf1: [line1.len]u8 = undefined;
1329 var buf2: [line2.len]u8 = undefined;1335 var buf2: [line2.len]u8 = undefined;
1330 var write_vecs = [_]posix.iovec_const{1336 var write_vecs: [2][]const u8 = .{ line1, line2 };
1331 .{1337 var read_vecs: [2][]u8 = .{ &buf2, &buf1 };
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 };
13501338
1351 var src_file = try tmp.dir.createFile("test.txt", .{ .read = true });1339 var src_file = try tmp.dir.createFile("test.txt", .{ .read = true });
1352 defer src_file.close();1340 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();
1355 try testing.expectEqual(@as(u64, line1.len + line2.len), try src_file.getEndPos());1346 try testing.expectEqual(@as(u64, line1.len + line2.len), try src_file.getEndPos());
1356 try src_file.seekTo(0);1347
1357 const read = try src_file.readvAll(&read_vecs);1348 var reader = writer.moveToReader(io);
1358 try testing.expectEqual(@as(usize, line1.len + line2.len), read);1349 try reader.seekTo(0);
1350 try reader.interface.readVecAll(&read_vecs);
1359 try testing.expectEqualStrings(&buf1, "line2\n");1351 try testing.expectEqualStrings(&buf1, "line2\n");
1360 try testing.expectEqualStrings(&buf2, "line1\n");1352 try testing.expectEqualStrings(&buf2, "line1\n");
1353 try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));
1361}1354}
13621355
1363test "pwritev, preadv" {1356test "pwritev, preadv" {
1357 const io = testing.io;
1358
1364 var tmp = tmpDir(.{});1359 var tmp = tmpDir(.{});
1365 defer tmp.cleanup();1360 defer tmp.cleanup();
13661361
1367 const line1 = "line1\n";1362 const line1 = "line1\n";
1368 const line2 = "line2\n";1363 const line2 = "line2\n";
13691364 var lines: [2][]const u8 = .{ line1, line2 };
1370 var buf1: [line1.len]u8 = undefined;1365 var buf1: [line1.len]u8 = undefined;
1371 var buf2: [line2.len]u8 = undefined;1366 var buf2: [line2.len]u8 = undefined;
1372 var write_vecs = [_]posix.iovec_const{1367 var read_vecs: [2][]u8 = .{ &buf2, &buf1 };
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 };
13921368
1393 var src_file = try tmp.dir.createFile("test.txt", .{ .read = true });1369 var src_file = try tmp.dir.createFile("test.txt", .{ .read = true });
1394 defer src_file.close();1370 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();
1397 try testing.expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.getEndPos());1377 try testing.expectEqual(@as(u64, 16 + line1.len + line2.len), try src_file.getEndPos());
1398 const read = try src_file.preadvAll(&read_vecs, 16);1378
1399 try testing.expectEqual(@as(usize, line1.len + line2.len), read);1379 var reader = writer.moveToReader(io);
1380 try reader.seekTo(16);
1381 try reader.interface.readVecAll(&read_vecs);
1400 try testing.expectEqualStrings(&buf1, "line2\n");1382 try testing.expectEqualStrings(&buf1, "line2\n");
1401 try testing.expectEqualStrings(&buf2, "line1\n");1383 try testing.expectEqualStrings(&buf2, "line1\n");
1384 try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1));
1402}1385}
14031386
1404test "setEndPos" {1387test "setEndPos" {
...@@ -1406,6 +1389,8 @@ test "setEndPos" {...@@ -1406,6 +1389,8 @@ test "setEndPos" {
1406 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;1389 if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
1407 if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/238061390 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
1409 var tmp = tmpDir(.{});1394 var tmp = tmpDir(.{});
1410 defer tmp.cleanup();1395 defer tmp.cleanup();
14111396
...@@ -1416,11 +1401,13 @@ test "setEndPos" {...@@ -1416,11 +1401,13 @@ test "setEndPos" {
14161401
1417 const initial_size = try f.getEndPos();1402 const initial_size = try f.getEndPos();
1418 var buffer: [32]u8 = undefined;1403 var buffer: [32]u8 = undefined;
1404 var reader = f.reader(io, &.{});
14191405
1420 {1406 {
1421 try f.setEndPos(initial_size);1407 try f.setEndPos(initial_size);
1422 try testing.expectEqual(initial_size, try f.getEndPos());1408 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));
1424 try testing.expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]);1411 try testing.expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]);
1425 }1412 }
14261413
...@@ -1428,7 +1415,8 @@ test "setEndPos" {...@@ -1428,7 +1415,8 @@ test "setEndPos" {
1428 const larger = initial_size + 4;1415 const larger = initial_size + 4;
1429 try f.setEndPos(larger);1416 try f.setEndPos(larger);
1430 try testing.expectEqual(larger, try f.getEndPos());1417 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));
1432 try testing.expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]);1420 try testing.expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]);
1433 }1421 }
14341422
...@@ -1436,25 +1424,21 @@ test "setEndPos" {...@@ -1436,25 +1424,21 @@ test "setEndPos" {
1436 const smaller = initial_size - 5;1424 const smaller = initial_size - 5;
1437 try f.setEndPos(smaller);1425 try f.setEndPos(smaller);
1438 try testing.expectEqual(smaller, try f.getEndPos());1426 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));
1440 try testing.expectEqualStrings("nine", buffer[0..@intCast(smaller)]);1429 try testing.expectEqualStrings("nine", buffer[0..@intCast(smaller)]);
1441 }1430 }
14421431
1443 try f.setEndPos(0);1432 try f.setEndPos(0);
1444 try testing.expectEqual(0, try f.getEndPos());1433 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
1447 // Invalid file length should error gracefully. Actual limit is host1437 // Invalid file length should error gracefully. Actual limit is host
1448 // and file-system dependent, but 1PB should fail on filesystems like1438 // and file-system dependent, but 1PB should fail on filesystems like
1449 // EXT4 and NTFS. But XFS or Btrfs support up to 8EiB files.1439 // 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) {1440 try testing.expectError(error.FileTooBig, f.setEndPos(0x4_0000_0000_0000));
1451 return err;1441 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63)));
1452 };
1453
1454 f.setEndPos(std.math.maxInt(u63)) catch |err| if (err != error.FileTooBig) {
1455 return err;
1456 };
1457
1458 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63) + 1));1442 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63) + 1));
1459 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u64)));1443 try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u64)));
1460}1444}
...@@ -1560,31 +1544,6 @@ test "sendfile with buffered data" {...@@ -1560,31 +1544,6 @@ test "sendfile with buffered data" {
1560 try std.testing.expectEqualSlices(u8, "AAAA", written_buf[0..amt]);1544 try std.testing.expectEqualSlices(u8, "AAAA", written_buf[0..amt]);
1561}1545}
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
1588test "copyFile" {1547test "copyFile" {
1589 try testWithAllSupportedPathTypes(struct {1548 try testWithAllSupportedPathTypes(struct {
1590 fn impl(ctx: *TestContext) !void {1549 fn impl(ctx: *TestContext) !void {
...@@ -1708,8 +1667,8 @@ test "open file with exclusive lock twice, make sure second lock waits" {...@@ -1708,8 +1667,8 @@ test "open file with exclusive lock twice, make sure second lock waits" {
1708 }1667 }
1709 };1668 };
17101669
1711 var started = std.Thread.ResetEvent{};1670 var started: std.Thread.ResetEvent = .unset;
1712 var locked = std.Thread.ResetEvent{};1671 var locked: std.Thread.ResetEvent = .unset;
17131672
1714 const t = try std.Thread.spawn(.{}, S.checkFn, .{1673 const t = try std.Thread.spawn(.{}, S.checkFn, .{
1715 &ctx.dir,1674 &ctx.dir,
...@@ -1773,7 +1732,7 @@ test "read from locked file" {...@@ -1773,7 +1732,7 @@ test "read from locked file" {
1773 const f = try ctx.dir.createFile(filename, .{ .read = true });1732 const f = try ctx.dir.createFile(filename, .{ .read = true });
1774 defer f.close();1733 defer f.close();
1775 var buffer: [1]u8 = undefined;1734 var buffer: [1]u8 = undefined;
1776 _ = try f.readAll(&buffer);1735 _ = try f.read(&buffer);
1777 }1736 }
1778 {1737 {
1779 const f = try ctx.dir.createFile(filename, .{1738 const f = try ctx.dir.createFile(filename, .{
...@@ -1785,9 +1744,9 @@ test "read from locked file" {...@@ -1785,9 +1744,9 @@ test "read from locked file" {
1785 defer f2.close();1744 defer f2.close();
1786 var buffer: [1]u8 = undefined;1745 var buffer: [1]u8 = undefined;
1787 if (builtin.os.tag == .windows) {1746 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));
1789 } else {1748 } else {
1790 try std.testing.expectEqual(0, f2.readAll(&buffer));1749 try std.testing.expectEqual(0, f2.read(&buffer));
1791 }1750 }
1792 }1751 }
1793 }1752 }
...@@ -1944,6 +1903,7 @@ test "'.' and '..' in fs.Dir functions" {...@@ -1944,6 +1903,7 @@ test "'.' and '..' in fs.Dir functions" {
19441903
1945 try testWithAllSupportedPathTypes(struct {1904 try testWithAllSupportedPathTypes(struct {
1946 fn impl(ctx: *TestContext) !void {1905 fn impl(ctx: *TestContext) !void {
1906 const io = ctx.io;
1947 const subdir_path = try ctx.transformPath("./subdir");1907 const subdir_path = try ctx.transformPath("./subdir");
1948 const file_path = try ctx.transformPath("./subdir/../file");1908 const file_path = try ctx.transformPath("./subdir/../file");
1949 const copy_path = try ctx.transformPath("./subdir/../copy");1909 const copy_path = try ctx.transformPath("./subdir/../copy");
...@@ -1966,7 +1926,8 @@ test "'.' and '..' in fs.Dir functions" {...@@ -1966,7 +1926,8 @@ test "'.' and '..' in fs.Dir functions" {
1966 try ctx.dir.deleteFile(rename_path);1926 try ctx.dir.deleteFile(rename_path);
19671927
1968 try ctx.dir.writeFile(.{ .sub_path = update_path, .data = "something" });1928 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, .{});
1970 try testing.expectEqual(fs.Dir.PrevStatus.stale, prev_status);1931 try testing.expectEqual(fs.Dir.PrevStatus.stale, prev_status);
19711932
1972 try ctx.dir.deleteDir(subdir_path);1933 try ctx.dir.deleteDir(subdir_path);
...@@ -2005,13 +1966,6 @@ test "'.' and '..' in absolute functions" {...@@ -2005,13 +1966,6 @@ test "'.' and '..' in absolute functions" {
2005 renamed_file.close();1966 renamed_file.close();
2006 try fs.deleteFileAbsolute(renamed_file_path);1967 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
2015 try fs.deleteDirAbsolute(subdir_path);1969 try fs.deleteDirAbsolute(subdir_path);
2016}1970}
20171971
...@@ -2079,6 +2033,7 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2079,6 +2033,7 @@ test "invalid UTF-8/WTF-8 paths" {
20792033
2080 try testWithAllSupportedPathTypes(struct {2034 try testWithAllSupportedPathTypes(struct {
2081 fn impl(ctx: *TestContext) !void {2035 fn impl(ctx: *TestContext) !void {
2036 const io = ctx.io;
2082 // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte2037 // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte
2083 const invalid_path = try ctx.transformPath("\xFF");2038 const invalid_path = try ctx.transformPath("\xFF");
20842039
...@@ -2129,7 +2084,8 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2129,7 +2084,8 @@ test "invalid UTF-8/WTF-8 paths" {
2129 try testing.expectError(expected_err, ctx.dir.access(invalid_path, .{}));2084 try testing.expectError(expected_err, ctx.dir.access(invalid_path, .{}));
2130 try testing.expectError(expected_err, ctx.dir.accessZ(invalid_path, .{}));2085 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, .{}));
2133 try testing.expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, .{}));2089 try testing.expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, .{}));
21342090
2135 try testing.expectError(expected_err, ctx.dir.statFile(invalid_path));2091 try testing.expectError(expected_err, ctx.dir.statFile(invalid_path));
...@@ -2144,7 +2100,6 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2144,7 +2100,6 @@ test "invalid UTF-8/WTF-8 paths" {
2144 try testing.expectError(expected_err, fs.renameZ(ctx.dir, invalid_path, ctx.dir, invalid_path));2100 try testing.expectError(expected_err, fs.renameZ(ctx.dir, invalid_path, ctx.dir, invalid_path));
21452101
2146 if (native_os != .wasi and ctx.path_type != .relative) {2102 if (native_os != .wasi and ctx.path_type != .relative) {
2147 try testing.expectError(expected_err, fs.updateFileAbsolute(invalid_path, invalid_path, .{}));
2148 try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{}));2103 try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{}));
2149 try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path));2104 try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path));
2150 try testing.expectError(expected_err, fs.makeDirAbsoluteZ(invalid_path));2105 try testing.expectError(expected_err, fs.makeDirAbsoluteZ(invalid_path));
...@@ -2175,6 +2130,8 @@ test "invalid UTF-8/WTF-8 paths" {...@@ -2175,6 +2130,8 @@ test "invalid UTF-8/WTF-8 paths" {
2175}2130}
21762131
2177test "read file non vectored" {2132test "read file non vectored" {
2133 const io = std.testing.io;
2134
2178 var tmp_dir = testing.tmpDir(.{});2135 var tmp_dir = testing.tmpDir(.{});
2179 defer tmp_dir.cleanup();2136 defer tmp_dir.cleanup();
21802137
...@@ -2188,7 +2145,7 @@ test "read file non vectored" {...@@ -2188,7 +2145,7 @@ test "read file non vectored" {
2188 try file_writer.interface.flush();2145 try file_writer.interface.flush();
2189 }2146 }
21902147
2191 var file_reader: std.fs.File.Reader = .init(file, &.{});2148 var file_reader: std.Io.File.Reader = .initAdapted(file, io, &.{});
21922149
2193 var write_buffer: [100]u8 = undefined;2150 var write_buffer: [100]u8 = undefined;
2194 var w: std.Io.Writer = .fixed(&write_buffer);2151 var w: std.Io.Writer = .fixed(&write_buffer);
...@@ -2205,6 +2162,8 @@ test "read file non vectored" {...@@ -2205,6 +2162,8 @@ test "read file non vectored" {
2205}2162}
22062163
2207test "seek keeping partial buffer" {2164test "seek keeping partial buffer" {
2165 const io = std.testing.io;
2166
2208 var tmp_dir = testing.tmpDir(.{});2167 var tmp_dir = testing.tmpDir(.{});
2209 defer tmp_dir.cleanup();2168 defer tmp_dir.cleanup();
22102169
...@@ -2219,7 +2178,7 @@ test "seek keeping partial buffer" {...@@ -2219,7 +2178,7 @@ test "seek keeping partial buffer" {
2219 }2178 }
22202179
2221 var read_buffer: [3]u8 = undefined;2180 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
2224 try testing.expectEqual(0, file_reader.logicalPos());2183 try testing.expectEqual(0, file_reader.logicalPos());
22252184
...@@ -2246,13 +2205,15 @@ test "seek keeping partial buffer" {...@@ -2246,13 +2205,15 @@ test "seek keeping partial buffer" {
2246}2205}
22472206
2248test "seekBy" {2207test "seekBy" {
2208 const io = testing.io;
2209
2249 var tmp_dir = testing.tmpDir(.{});2210 var tmp_dir = testing.tmpDir(.{});
2250 defer tmp_dir.cleanup();2211 defer tmp_dir.cleanup();
22512212
2252 try tmp_dir.dir.writeFile(.{ .sub_path = "blah.txt", .data = "let's test seekBy" });2213 try tmp_dir.dir.writeFile(.{ .sub_path = "blah.txt", .data = "let's test seekBy" });
2253 const f = try tmp_dir.dir.openFile("blah.txt", .{ .mode = .read_only });2214 const f = try tmp_dir.dir.openFile("blah.txt", .{ .mode = .read_only });
2254 defer f.close();2215 defer f.close();
2255 var reader = f.readerStreaming(&.{});2216 var reader = f.readerStreaming(io, &.{});
2256 try reader.seekBy(2);2217 try reader.seekBy(2);
22572218
2258 var buffer: [20]u8 = undefined;2219 var buffer: [20]u8 = undefined;
lib/std/http/Client.zig+20-25
...@@ -247,6 +247,7 @@ pub const Connection = struct {...@@ -247,6 +247,7 @@ pub const Connection = struct {
247 port: u16,247 port: u16,
248 stream: Io.net.Stream,248 stream: Io.net.Stream,
249 ) error{OutOfMemory}!*Plain {249 ) error{OutOfMemory}!*Plain {
250 const io = client.io;
250 const gpa = client.allocator;251 const gpa = client.allocator;
251 const alloc_len = allocLen(client, remote_host.bytes.len);252 const alloc_len = allocLen(client, remote_host.bytes.len);
252 const base = try gpa.alignedAlloc(u8, .of(Plain), alloc_len);253 const base = try gpa.alignedAlloc(u8, .of(Plain), alloc_len);
...@@ -260,8 +261,8 @@ pub const Connection = struct {...@@ -260,8 +261,8 @@ pub const Connection = struct {
260 plain.* = .{261 plain.* = .{
261 .connection = .{262 .connection = .{
262 .client = client,263 .client = client,
263 .stream_writer = stream.writer(socket_write_buffer),264 .stream_writer = stream.writer(io, socket_write_buffer),
264 .stream_reader = stream.reader(socket_read_buffer),265 .stream_reader = stream.reader(io, socket_read_buffer),
265 .pool_node = .{},266 .pool_node = .{},
266 .port = port,267 .port = port,
267 .host_len = @intCast(remote_host.bytes.len),268 .host_len = @intCast(remote_host.bytes.len),
...@@ -300,6 +301,7 @@ pub const Connection = struct {...@@ -300,6 +301,7 @@ pub const Connection = struct {
300 port: u16,301 port: u16,
301 stream: Io.net.Stream,302 stream: Io.net.Stream,
302 ) error{ OutOfMemory, TlsInitializationFailed }!*Tls {303 ) error{ OutOfMemory, TlsInitializationFailed }!*Tls {
304 const io = client.io;
303 const gpa = client.allocator;305 const gpa = client.allocator;
304 const alloc_len = allocLen(client, remote_host.bytes.len);306 const alloc_len = allocLen(client, remote_host.bytes.len);
305 const base = try gpa.alignedAlloc(u8, .of(Tls), alloc_len);307 const base = try gpa.alignedAlloc(u8, .of(Tls), alloc_len);
...@@ -316,11 +318,14 @@ pub const Connection = struct {...@@ -316,11 +318,14 @@ pub const Connection = struct {
316 assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);318 assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len);
317 @memcpy(host_buffer, remote_host.bytes);319 @memcpy(host_buffer, remote_host.bytes);
318 const tls: *Tls = @ptrCast(base);320 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;
319 tls.* = .{324 tls.* = .{
320 .connection = .{325 .connection = .{
321 .client = client,326 .client = client,
322 .stream_writer = stream.writer(tls_write_buffer),327 .stream_writer = stream.writer(io, tls_write_buffer),
323 .stream_reader = stream.reader(socket_read_buffer),328 .stream_reader = stream.reader(io, socket_read_buffer),
324 .pool_node = .{},329 .pool_node = .{},
325 .port = port,330 .port = port,
326 .host_len = @intCast(remote_host.bytes.len),331 .host_len = @intCast(remote_host.bytes.len),
...@@ -338,6 +343,8 @@ pub const Connection = struct {...@@ -338,6 +343,8 @@ pub const Connection = struct {
338 .ssl_key_log = client.ssl_key_log,343 .ssl_key_log = client.ssl_key_log,
339 .read_buffer = tls_read_buffer,344 .read_buffer = tls_read_buffer,
340 .write_buffer = socket_write_buffer,345 .write_buffer = socket_write_buffer,
346 .entropy = &random_buffer,
347 .realtime_now_seconds = now_ts,
341 // This is appropriate for HTTPS because the HTTP headers contain348 // This is appropriate for HTTPS because the HTTP headers contain
342 // the content length which is used to detect truncation attacks.349 // the content length which is used to detect truncation attacks.
343 .allow_truncation_attacks = true,350 .allow_truncation_attacks = true,
...@@ -1390,16 +1397,8 @@ pub const basic_authorization = struct {...@@ -1390,16 +1397,8 @@ pub const basic_authorization = struct {
1390};1397};
13911398
1392pub const ConnectTcpError = error{1399pub const ConnectTcpError = error{
1393 ConnectionRefused,
1394 NetworkUnreachable,
1395 ConnectionTimedOut,
1396 ConnectionResetByPeer,
1397 TemporaryNameServerFailure,
1398 NameServerFailure,
1399 UnknownHostName,
1400 UnexpectedConnectFailure,
1401 TlsInitializationFailed,1400 TlsInitializationFailed,
1402} || Allocator.Error || Io.Cancelable;1401} || Allocator.Error || HostName.ConnectError;
14031402
1404/// Reuses a `Connection` if one matching `host` and `port` is already open.1403/// Reuses a `Connection` if one matching `host` and `port` is already open.
1405///1404///
...@@ -1424,6 +1423,7 @@ pub const ConnectTcpOptions = struct {...@@ -1424,6 +1423,7 @@ pub const ConnectTcpOptions = struct {
1424};1423};
14251424
1426pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcpError!*Connection {1425pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcpError!*Connection {
1426 const io = client.io;
1427 const host = options.host;1427 const host = options.host;
1428 const port = options.port;1428 const port = options.port;
1429 const protocol = options.protocol;1429 const protocol = options.protocol;
...@@ -1437,22 +1437,17 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp...@@ -1437,22 +1437,17 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp
1437 .protocol = protocol,1437 .protocol = protocol,
1438 })) |conn| return conn;1438 })) |conn| return conn;
14391439
1440 const stream = host.connect(client.io, port, .{ .mode = .stream }) catch |err| switch (err) {1440 var stream = try host.connect(io, port, .{ .mode = .stream });
1441 error.ConnectionRefused => return error.ConnectionRefused,1441 errdefer stream.close(io);
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();
14511442
1452 switch (protocol) {1443 switch (protocol) {
1453 .tls => {1444 .tls => {
1454 if (disable_tls) return error.TlsInitializationFailed;1445 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 };
1456 client.connection_pool.addUsed(&tc.connection);1451 client.connection_pool.addUsed(&tc.connection);
1457 return &tc.connection;1452 return &tc.connection;
1458 },1453 },
lib/std/os/linux/IoUring.zig+90-72
...@@ -3,7 +3,7 @@ const std = @import("std");...@@ -3,7 +3,7 @@ const std = @import("std");
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const mem = std.mem;5const mem = std.mem;
6const net = std.net;6const net = std.Io.net;
7const posix = std.posix;7const posix = std.posix;
8const linux = std.os.linux;8const linux = std.os.linux;
9const testing = std.testing;9const testing = std.testing;
...@@ -2361,19 +2361,22 @@ test "sendmsg/recvmsg" {...@@ -2361,19 +2361,22 @@ test "sendmsg/recvmsg" {
2361 };2361 };
2362 defer ring.deinit();2362 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);
2367 defer posix.close(server);2370 defer posix.close(server);
2368 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));2371 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
2369 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));2372 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
2372 // set address_server to the OS-chosen IP/port.2375 // set address_server to the OS-chosen IP/port.
2373 var slen: posix.socklen_t = address_server.getOsSockLen();2376 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
2374 try posix.getsockname(server, &address_server.any, &slen);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);
2377 defer posix.close(client);2380 defer posix.close(client);
23782381
2379 const buffer_send = [_]u8{42} ** 128;2382 const buffer_send = [_]u8{42} ** 128;
...@@ -2381,8 +2384,8 @@ test "sendmsg/recvmsg" {...@@ -2381,8 +2384,8 @@ test "sendmsg/recvmsg" {
2381 posix.iovec_const{ .base = &buffer_send, .len = buffer_send.len },2384 posix.iovec_const{ .base = &buffer_send, .len = buffer_send.len },
2382 };2385 };
2383 const msg_send: posix.msghdr_const = .{2386 const msg_send: posix.msghdr_const = .{
2384 .name = &address_server.any,2387 .name = addrAny(&address_server),
2385 .namelen = address_server.getOsSockLen(),2388 .namelen = @sizeOf(linux.sockaddr.in),
2386 .iov = &iovecs_send,2389 .iov = &iovecs_send,
2387 .iovlen = 1,2390 .iovlen = 1,
2388 .control = null,2391 .control = null,
...@@ -2398,11 +2401,13 @@ test "sendmsg/recvmsg" {...@@ -2398,11 +2401,13 @@ test "sendmsg/recvmsg" {
2398 var iovecs_recv = [_]posix.iovec{2401 var iovecs_recv = [_]posix.iovec{
2399 posix.iovec{ .base = &buffer_recv, .len = buffer_recv.len },2402 posix.iovec{ .base = &buffer_recv, .len = buffer_recv.len },
2400 };2403 };
2401 const addr = [_]u8{0} ** 4;2404 var address_recv: linux.sockaddr.in = .{
2402 var address_recv = net.Address.initIp4(addr, 0);2405 .port = 0,
2406 .addr = 0,
2407 };
2403 var msg_recv: posix.msghdr = .{2408 var msg_recv: posix.msghdr = .{
2404 .name = &address_recv.any,2409 .name = addrAny(&address_recv),
2405 .namelen = address_recv.getOsSockLen(),2410 .namelen = @sizeOf(linux.sockaddr.in),
2406 .iov = &iovecs_recv,2411 .iov = &iovecs_recv,
2407 .iovlen = 1,2412 .iovlen = 1,
2408 .control = null,2413 .control = null,
...@@ -2441,6 +2446,8 @@ test "sendmsg/recvmsg" {...@@ -2441,6 +2446,8 @@ test "sendmsg/recvmsg" {
2441test "timeout (after a relative time)" {2446test "timeout (after a relative time)" {
2442 if (!is_linux) return error.SkipZigTest;2447 if (!is_linux) return error.SkipZigTest;
24432448
2449 const io = testing.io;
2450
2444 var ring = IoUring.init(1, 0) catch |err| switch (err) {2451 var ring = IoUring.init(1, 0) catch |err| switch (err) {
2445 error.SystemOutdated => return error.SkipZigTest,2452 error.SystemOutdated => return error.SkipZigTest,
2446 error.PermissionDenied => return error.SkipZigTest,2453 error.PermissionDenied => return error.SkipZigTest,
...@@ -2452,12 +2459,12 @@ test "timeout (after a relative time)" {...@@ -2452,12 +2459,12 @@ test "timeout (after a relative time)" {
2452 const margin = 5;2459 const margin = 5;
2453 const ts: linux.kernel_timespec = .{ .sec = 0, .nsec = ms * 1000000 };2460 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);
2456 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);2463 const sqe = try ring.timeout(0x55555555, &ts, 0, 0);
2457 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe.opcode);2464 try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe.opcode);
2458 try testing.expectEqual(@as(u32, 1), try ring.submit());2465 try testing.expectEqual(@as(u32, 1), try ring.submit());
2459 const cqe = try ring.copy_cqe();2466 const cqe = try ring.copy_cqe();
2460 const stopped = std.time.milliTimestamp();2467 const stopped = try std.Io.Timestamp.now(io, .awake);
24612468
2462 try testing.expectEqual(linux.io_uring_cqe{2469 try testing.expectEqual(linux.io_uring_cqe{
2463 .user_data = 0x55555555,2470 .user_data = 0x55555555,
...@@ -2466,7 +2473,8 @@ test "timeout (after a relative time)" {...@@ -2466,7 +2473,8 @@ test "timeout (after a relative time)" {
2466 }, cqe);2473 }, cqe);
24672474
2468 // Tests should not depend on timings: skip test if outside margin.2475 // 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;
2470}2478}
24712479
2472test "timeout (after a number of completions)" {2480test "timeout (after a number of completions)" {
...@@ -2861,19 +2869,22 @@ test "shutdown" {...@@ -2861,19 +2869,22 @@ test "shutdown" {
2861 };2869 };
2862 defer ring.deinit();2870 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
2866 // Socket bound, expect shutdown to work2877 // Socket bound, expect shutdown to work
2867 {2878 {
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);
2869 defer posix.close(server);2880 defer posix.close(server);
2870 try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));2881 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));
2872 try posix.listen(server, 1);2883 try posix.listen(server, 1);
28732884
2874 // set address to the OS-chosen IP/port.2885 // set address to the OS-chosen IP/port.
2875 var slen: posix.socklen_t = address.getOsSockLen();2886 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
2876 try posix.getsockname(server, &address.any, &slen);2887 try posix.getsockname(server, addrAny(&address), &slen);
28772888
2878 const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD);2889 const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD);
2879 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);2890 try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
...@@ -2898,7 +2909,7 @@ test "shutdown" {...@@ -2898,7 +2909,7 @@ test "shutdown" {
28982909
2899 // Socket not bound, expect to fail with ENOTCONN2910 // Socket not bound, expect to fail with ENOTCONN
2900 {2911 {
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);
2902 defer posix.close(server);2913 defer posix.close(server);
29032914
2904 const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {2915 const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {
...@@ -2966,22 +2977,11 @@ test "renameat" {...@@ -2966,22 +2977,11 @@ test "renameat" {
2966 }, cqe);2977 }, cqe);
29672978
2968 // Validate that the old file doesn't exist anymore2979 // Validate that the old file doesn't exist anymore
2969 {2980 try testing.expectError(error.FileNotFound, tmp.dir.openFile(old_path, .{}));
2970 _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) {
2971 error.FileNotFound => {},
2972 else => std.debug.panic("unexpected error: {}", .{err}),
2973 };
2974 }
29752981
2976 // Validate that the new file exists with the proper content2982 // Validate that the new file exists with the proper content
2977 {2983 var new_file_data: [16]u8 = undefined;
2978 const new_file = try tmp.dir.openFile(new_path, .{});2984 try testing.expectEqualStrings("hello", try tmp.dir.readFile(new_path, &new_file_data));
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 }
2985}2985}
29862986
2987test "unlinkat" {2987test "unlinkat" {
...@@ -3179,12 +3179,8 @@ test "linkat" {...@@ -3179,12 +3179,8 @@ test "linkat" {
3179 }, cqe);3179 }, cqe);
31803180
3181 // Validate the second file3181 // Validate the second file
3182 const second_file = try tmp.dir.openFile(second_path, .{});
3183 defer second_file.close();
3184
3185 var second_file_data: [16]u8 = undefined;3182 var second_file_data: [16]u8 = undefined;
3186 const bytes_read = try second_file.readAll(&second_file_data);3183 try testing.expectEqualStrings("hello", try tmp.dir.readFile(second_path, &second_file_data));
3187 try testing.expectEqualStrings("hello", second_file_data[0..bytes_read]);
3188}3184}
31893185
3190test "provide_buffers: read" {3186test "provide_buffers: read" {
...@@ -3588,7 +3584,10 @@ const SocketTestHarness = struct {...@@ -3588,7 +3584,10 @@ const SocketTestHarness = struct {
35883584
3589fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {3585fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
3590 // Create a TCP server socket3586 // 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 };
3592 const listener_socket = try createListenerSocket(&address);3591 const listener_socket = try createListenerSocket(&address);
3593 errdefer posix.close(listener_socket);3592 errdefer posix.close(listener_socket);
35943593
...@@ -3598,9 +3597,9 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {...@@ -3598,9 +3597,9 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
3598 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);3597 _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);
35993598
3600 // Create a TCP client socket3599 // 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);
3602 errdefer posix.close(client);3601 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
3605 try testing.expectEqual(@as(u32, 2), try ring.submit());3604 try testing.expectEqual(@as(u32, 2), try ring.submit());
36063605
...@@ -3636,18 +3635,18 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {...@@ -3636,18 +3635,18 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
3636 };3635 };
3637}3636}
36383637
3639fn createListenerSocket(address: *net.Address) !posix.socket_t {3638fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t {
3640 const kernel_backlog = 1;3639 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);
3642 errdefer posix.close(listener_socket);3641 errdefer posix.close(listener_socket);
36433642
3644 try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));3643 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));
3646 try posix.listen(listener_socket, kernel_backlog);3645 try posix.listen(listener_socket, kernel_backlog);
36473646
3648 // set address to the OS-chosen IP/port.3647 // set address to the OS-chosen IP/port.
3649 var slen: posix.socklen_t = address.getOsSockLen();3648 var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
3650 try posix.getsockname(listener_socket, &address.any, &slen);3649 try posix.getsockname(listener_socket, addrAny(address), &slen);
36513650
3652 return listener_socket;3651 return listener_socket;
3653}3652}
...@@ -3662,7 +3661,10 @@ test "accept multishot" {...@@ -3662,7 +3661,10 @@ test "accept multishot" {
3662 };3661 };
3663 defer ring.deinit();3662 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 };
3666 const listener_socket = try createListenerSocket(&address);3668 const listener_socket = try createListenerSocket(&address);
3667 defer posix.close(listener_socket);3669 defer posix.close(listener_socket);
36683670
...@@ -3676,9 +3678,9 @@ test "accept multishot" {...@@ -3676,9 +3678,9 @@ test "accept multishot" {
3676 var nr: usize = 4; // number of clients to connect3678 var nr: usize = 4; // number of clients to connect
3677 while (nr > 0) : (nr -= 1) {3679 while (nr > 0) : (nr -= 1) {
3678 // connect client3680 // 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);
3680 errdefer posix.close(client);3682 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
3683 // test accept completion3685 // test accept completion
3684 var cqe = try ring.copy_cqe();3686 var cqe = try ring.copy_cqe();
...@@ -3756,7 +3758,10 @@ test "accept_direct" {...@@ -3756,7 +3758,10 @@ test "accept_direct" {
3756 else => return err,3758 else => return err,
3757 };3759 };
3758 defer ring.deinit();3760 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
3761 // register direct file descriptors3766 // register direct file descriptors
3762 var registered_fds = [_]posix.fd_t{-1} ** 2;3767 var registered_fds = [_]posix.fd_t{-1} ** 2;
...@@ -3779,8 +3784,8 @@ test "accept_direct" {...@@ -3779,8 +3784,8 @@ test "accept_direct" {
3779 try testing.expectEqual(@as(u32, 1), try ring.submit());3784 try testing.expectEqual(@as(u32, 1), try ring.submit());
37803785
3781 // connect3786 // connect
3782 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);3787 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3783 try posix.connect(client, &address.any, address.getOsSockLen());3788 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
3784 defer posix.close(client);3789 defer posix.close(client);
37853790
3786 // accept completion3791 // accept completion
...@@ -3813,8 +3818,8 @@ test "accept_direct" {...@@ -3813,8 +3818,8 @@ test "accept_direct" {
3813 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);3818 _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);
3814 try testing.expectEqual(@as(u32, 1), try ring.submit());3819 try testing.expectEqual(@as(u32, 1), try ring.submit());
3815 // connect3820 // connect
3816 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);3821 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3817 try posix.connect(client, &address.any, address.getOsSockLen());3822 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
3818 defer posix.close(client);3823 defer posix.close(client);
3819 // completion with error3824 // completion with error
3820 const cqe_accept = try ring.copy_cqe();3825 const cqe_accept = try ring.copy_cqe();
...@@ -3837,7 +3842,10 @@ test "accept_multishot_direct" {...@@ -3837,7 +3842,10 @@ test "accept_multishot_direct" {
3837 };3842 };
3838 defer ring.deinit();3843 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
3842 var registered_fds = [_]posix.fd_t{-1} ** 2;3850 var registered_fds = [_]posix.fd_t{-1} ** 2;
3843 try ring.register_files(registered_fds[0..]);3851 try ring.register_files(registered_fds[0..]);
...@@ -3855,8 +3863,8 @@ test "accept_multishot_direct" {...@@ -3855,8 +3863,8 @@ test "accept_multishot_direct" {
38553863
3856 for (registered_fds) |_| {3864 for (registered_fds) |_| {
3857 // connect3865 // connect
3858 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);3866 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3859 try posix.connect(client, &address.any, address.getOsSockLen());3867 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
3860 defer posix.close(client);3868 defer posix.close(client);
38613869
3862 // accept completion3870 // accept completion
...@@ -3870,8 +3878,8 @@ test "accept_multishot_direct" {...@@ -3870,8 +3878,8 @@ test "accept_multishot_direct" {
3870 // Multishot is terminated (more flag is not set).3878 // Multishot is terminated (more flag is not set).
3871 {3879 {
3872 // connect3880 // connect
3873 const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);3881 const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
3874 try posix.connect(client, &address.any, address.getOsSockLen());3882 try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
3875 defer posix.close(client);3883 defer posix.close(client);
3876 // completion with error3884 // completion with error
3877 const cqe_accept = try ring.copy_cqe();3885 const cqe_accept = try ring.copy_cqe();
...@@ -3944,7 +3952,10 @@ test "socket_direct/socket_direct_alloc/close_direct" {...@@ -3944,7 +3952,10 @@ test "socket_direct/socket_direct_alloc/close_direct" {
3944 try testing.expect(cqe_socket.res == 2); // returns registered file index3952 try testing.expect(cqe_socket.res == 2); // returns registered file index
39453953
3946 // use sockets from registered_fds in connect operation3954 // 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 };
3948 const listener_socket = try createListenerSocket(&address);3959 const listener_socket = try createListenerSocket(&address);
3949 defer posix.close(listener_socket);3960 defer posix.close(listener_socket);
3950 const accept_userdata: u64 = 0xaaaaaaaa;3961 const accept_userdata: u64 = 0xaaaaaaaa;
...@@ -3954,7 +3965,7 @@ test "socket_direct/socket_direct_alloc/close_direct" {...@@ -3954,7 +3965,7 @@ test "socket_direct/socket_direct_alloc/close_direct" {
3954 // prepare accept3965 // prepare accept
3955 _ = try ring.accept(accept_userdata, listener_socket, null, null, 0);3966 _ = try ring.accept(accept_userdata, listener_socket, null, null, 0);
3956 // prepare connect with fixed socket3967 // 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));
3958 connect_sqe.flags |= linux.IOSQE_FIXED_FILE; // fd is fixed file index3969 connect_sqe.flags |= linux.IOSQE_FIXED_FILE; // fd is fixed file index
3959 // submit both3970 // submit both
3960 try testing.expectEqual(@as(u32, 2), try ring.submit());3971 try testing.expectEqual(@as(u32, 2), try ring.submit());
...@@ -4483,12 +4494,15 @@ test "bind/listen/connect" {...@@ -4483,12 +4494,15 @@ test "bind/listen/connect" {
4483 // LISTEN is higher required operation4494 // LISTEN is higher required operation
4484 if (!probe.is_supported(.LISTEN)) return error.SkipZigTest;4495 if (!probe.is_supported(.LISTEN)) return error.SkipZigTest;
44854496
4486 var addr = net.Address.initIp4([4]u8{ 127, 0, 0, 1 }, 0);4497 var addr: linux.sockaddr.in = .{
4487 const proto: u32 = if (addr.any.family == linux.AF.UNIX) 0 else linux.IPPROTO.TCP;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
4489 const listen_fd = brk: {4503 const listen_fd = brk: {
4490 // Create socket4504 // 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);
4492 try testing.expectEqual(1, try ring.submit());4506 try testing.expectEqual(1, try ring.submit());
4493 var cqe = try ring.copy_cqe();4507 var cqe = try ring.copy_cqe();
4494 try testing.expectEqual(1, cqe.user_data);4508 try testing.expectEqual(1, cqe.user_data);
...@@ -4500,7 +4514,7 @@ test "bind/listen/connect" {...@@ -4500,7 +4514,7 @@ test "bind/listen/connect" {
4500 var optval: u32 = 1;4514 var optval: u32 = 1;
4501 (try ring.setsockopt(2, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval))).link_next();4515 (try ring.setsockopt(2, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval))).link_next();
4502 (try ring.setsockopt(3, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEPORT, mem.asBytes(&optval))).link_next();4516 (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();
4504 _ = try ring.listen(5, listen_fd, 1, 0);4518 _ = try ring.listen(5, listen_fd, 1, 0);
4505 // Submit 4 operations4519 // Submit 4 operations
4506 try testing.expectEqual(4, try ring.submit());4520 try testing.expectEqual(4, try ring.submit());
...@@ -4521,15 +4535,15 @@ test "bind/listen/connect" {...@@ -4521,15 +4535,15 @@ test "bind/listen/connect" {
4521 try testing.expectEqual(1, optval);4535 try testing.expectEqual(1, optval);
45224536
4523 // Read system assigned port into addr4537 // Read system assigned port into addr
4524 var addr_len: posix.socklen_t = addr.getOsSockLen();4538 var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in);
4525 try posix.getsockname(listen_fd, &addr.any, &addr_len);4539 try posix.getsockname(listen_fd, addrAny(&addr), &addr_len);
45264540
4527 break :brk listen_fd;4541 break :brk listen_fd;
4528 };4542 };
45294543
4530 const connect_fd = brk: {4544 const connect_fd = brk: {
4531 // Create connect socket4545 // 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);
4533 try testing.expectEqual(1, try ring.submit());4547 try testing.expectEqual(1, try ring.submit());
4534 const cqe = try ring.copy_cqe();4548 const cqe = try ring.copy_cqe();
4535 try testing.expectEqual(6, cqe.user_data);4549 try testing.expectEqual(6, cqe.user_data);
...@@ -4542,7 +4556,7 @@ test "bind/listen/connect" {...@@ -4542,7 +4556,7 @@ test "bind/listen/connect" {
45424556
4543 // Prepare accept/connect operations4557 // Prepare accept/connect operations
4544 _ = try ring.accept(7, listen_fd, null, null, 0);4558 _ = 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));
4546 try testing.expectEqual(2, try ring.submit());4560 try testing.expectEqual(2, try ring.submit());
4547 // Get listener accepted socket4561 // Get listener accepted socket
4548 var accept_fd: posix.socket_t = 0;4562 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...@@ -4604,3 +4618,7 @@ fn testSendRecv(ring: *IoUring, send_fd: posix.socket_t, recv_fd: posix.socket_t
4604 try testing.expectEqualSlices(u8, buffer_send, buffer_recv[0..buffer_send.len]);4618 try testing.expectEqualSlices(u8, buffer_send, buffer_recv[0..buffer_send.len]);
4605 try testing.expectEqualSlices(u8, buffer_send, buffer_recv[buffer_send.len..]);4619 try testing.expectEqualSlices(u8, buffer_send, buffer_recv[buffer_send.len..]);
4606}4620}
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...@@ -3000,31 +3000,7 @@ pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: mode_t) MakeDirErro
3000 windows.CloseHandle(sub_dir_handle);3000 windows.CloseHandle(sub_dir_handle);
3001}3001}
30023002
3003pub const MakeDirError = error{3003pub const MakeDirError = std.Io.Dir.MakeError;
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;
30283004
3029/// Create a directory.3005/// Create a directory.
3030/// `mode` is ignored on Windows and WASI.3006/// `mode` is ignored on Windows and WASI.
lib/std/posix/test.zig+2-5
...@@ -731,11 +731,8 @@ test "dup & dup2" {...@@ -731,11 +731,8 @@ test "dup & dup2" {
731 try dup2ed.writeAll("dup2");731 try dup2ed.writeAll("dup2");
732 }732 }
733733
734 var file = try tmp.dir.openFile("os_dup_test", .{});734 var buffer: [8]u8 = undefined;
735 defer file.close();735 try testing.expectEqualStrings("dupdup2", try tmp.dir.readFile("os_dup_test", &buffer));
736
737 var buf: [7]u8 = undefined;
738 try testing.expectEqualStrings("dupdup2", buf[0..try file.readAll(&buf)]);
739}736}
740737
741test "writev longer than IOV_MAX" {738test "writev longer than IOV_MAX" {
lib/std/process/Child.zig+26-11
...@@ -1,5 +1,9 @@...@@ -1,5 +1,9 @@
1const std = @import("../std.zig");1const ChildProcess = @This();
2
2const builtin = @import("builtin");3const builtin = @import("builtin");
4const native_os = builtin.os.tag;
5
6const std = @import("../std.zig");
3const unicode = std.unicode;7const unicode = std.unicode;
4const fs = std.fs;8const fs = std.fs;
5const process = std.process;9const process = std.process;
...@@ -11,9 +15,7 @@ const mem = std.mem;...@@ -11,9 +15,7 @@ const mem = std.mem;
11const EnvMap = std.process.EnvMap;15const EnvMap = std.process.EnvMap;
12const maxInt = std.math.maxInt;16const maxInt = std.math.maxInt;
13const assert = std.debug.assert;17const assert = std.debug.assert;
14const native_os = builtin.os.tag;
15const Allocator = std.mem.Allocator;18const Allocator = std.mem.Allocator;
16const ChildProcess = @This();
17const ArrayList = std.ArrayList;19const ArrayList = std.ArrayList;
1820
19pub const Id = switch (native_os) {21pub const Id = switch (native_os) {
...@@ -317,16 +319,23 @@ pub fn waitForSpawn(self: *ChildProcess) SpawnError!void {...@@ -317,16 +319,23 @@ pub fn waitForSpawn(self: *ChildProcess) SpawnError!void {
317319
318 const err_pipe = self.err_pipe orelse return;320 const err_pipe = self.err_pipe orelse return;
319 self.err_pipe = null;321 self.err_pipe = null;
320
321 // Wait for the child to report any errors in or before `execvpe`.322 // Wait for the child to report any errors in or before `execvpe`.
322 if (readIntFd(err_pipe)) |child_err_int| {323 const report = readIntFd(err_pipe);
323 posix.close(err_pipe);324 posix.close(err_pipe);
325 if (report) |child_err_int| {
324 const child_err: SpawnError = @errorCast(@errorFromInt(child_err_int));326 const child_err: SpawnError = @errorCast(@errorFromInt(child_err_int));
325 self.term = child_err;327 self.term = child_err;
326 return child_err;328 return child_err;
327 } else |_| {329 } else |read_err| switch (read_err) {
328 // Write end closed by CLOEXEC at the time of the `execvpe` call, indicating success!330 error.EndOfStream => {
329 posix.close(err_pipe);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 },
330 }339 }
331}340}
332341
...@@ -1014,8 +1023,14 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {...@@ -1014,8 +1023,14 @@ fn writeIntFd(fd: i32, value: ErrInt) !void {
10141023
1015fn readIntFd(fd: i32) !ErrInt {1024fn readIntFd(fd: i32) !ErrInt {
1016 var buffer: [8]u8 = undefined;1025 var buffer: [8]u8 = undefined;
1017 var fr: std.fs.File.Reader = .initStreaming(.{ .handle = fd }, &buffer);1026 var i: usize = 0;
1018 return @intCast(fr.interface.takeInt(u64, .little) catch return error.SystemResources);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);
1019}1034}
10201035
1021const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);1036const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
lib/std/tar/Writer.zig+20-18
...@@ -1,7 +1,9 @@...@@ -1,7 +1,9 @@
1const Writer = @This();
2
1const std = @import("std");3const std = @import("std");
4const Io = std.Io;
2const assert = std.debug.assert;5const assert = std.debug.assert;
3const testing = std.testing;6const testing = std.testing;
4const Writer = @This();
57
6const block_size = @sizeOf(Header);8const block_size = @sizeOf(Header);
79
...@@ -14,7 +16,7 @@ pub const Options = struct {...@@ -14,7 +16,7 @@ pub const Options = struct {
14 mtime: u64 = 0,16 mtime: u64 = 0,
15};17};
1618
17underlying_writer: *std.Io.Writer,19underlying_writer: *Io.Writer,
18prefix: []const u8 = "",20prefix: []const u8 = "",
19mtime_now: u64 = 0,21mtime_now: u64 = 0,
2022
...@@ -36,12 +38,12 @@ pub fn writeDir(w: *Writer, sub_path: []const u8, options: Options) Error!void {...@@ -36,12 +38,12 @@ pub fn writeDir(w: *Writer, sub_path: []const u8, options: Options) Error!void {
36 try w.writeHeader(.directory, sub_path, "", 0, options);38 try w.writeHeader(.directory, sub_path, "", 0, options);
37}39}
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
41pub fn writeFile(43pub fn writeFile(
42 w: *Writer,44 w: *Writer,
43 sub_path: []const u8,45 sub_path: []const u8,
44 file_reader: *std.fs.File.Reader,46 file_reader: *Io.File.Reader,
45 stat_mtime: i128,47 stat_mtime: i128,
46) WriteFileError!void {48) WriteFileError!void {
47 const size = try file_reader.getSize();49 const size = try file_reader.getSize();
...@@ -58,7 +60,7 @@ pub fn writeFile(...@@ -58,7 +60,7 @@ pub fn writeFile(
58 try w.writePadding64(size);60 try w.writePadding64(size);
59}61}
6062
61pub const WriteFileStreamError = Error || std.Io.Reader.StreamError;63pub const WriteFileStreamError = Error || Io.Reader.StreamError;
6264
63/// Writes file reading file content from `reader`. Reads exactly `size` bytes65/// Writes file reading file content from `reader`. Reads exactly `size` bytes
64/// from `reader`, or returns `error.EndOfStream`.66/// from `reader`, or returns `error.EndOfStream`.
...@@ -66,7 +68,7 @@ pub fn writeFileStream(...@@ -66,7 +68,7 @@ pub fn writeFileStream(
66 w: *Writer,68 w: *Writer,
67 sub_path: []const u8,69 sub_path: []const u8,
68 size: u64,70 size: u64,
69 reader: *std.Io.Reader,71 reader: *Io.Reader,
70 options: Options,72 options: Options,
71) WriteFileStreamError!void {73) WriteFileStreamError!void {
72 try w.writeHeader(.regular, sub_path, "", size, options);74 try w.writeHeader(.regular, sub_path, "", size, options);
...@@ -136,15 +138,15 @@ fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const [...@@ -136,15 +138,15 @@ fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const [
136 try w.writePadding(len);138 try w.writePadding(len);
137}139}
138140
139fn writePadding(w: *Writer, bytes: usize) std.Io.Writer.Error!void {141fn writePadding(w: *Writer, bytes: usize) Io.Writer.Error!void {
140 return writePaddingPos(w, bytes % block_size);142 return writePaddingPos(w, bytes % block_size);
141}143}
142144
143fn writePadding64(w: *Writer, bytes: u64) std.Io.Writer.Error!void {145fn writePadding64(w: *Writer, bytes: u64) Io.Writer.Error!void {
144 return writePaddingPos(w, @intCast(bytes % block_size));146 return writePaddingPos(w, @intCast(bytes % block_size));
145}147}
146148
147fn writePaddingPos(w: *Writer, pos: usize) std.Io.Writer.Error!void {149fn writePaddingPos(w: *Writer, pos: usize) Io.Writer.Error!void {
148 if (pos == 0) return;150 if (pos == 0) return;
149 try w.underlying_writer.splatByteAll(0, block_size - pos);151 try w.underlying_writer.splatByteAll(0, block_size - pos);
150}152}
...@@ -153,7 +155,7 @@ fn writePaddingPos(w: *Writer, pos: usize) std.Io.Writer.Error!void {...@@ -153,7 +155,7 @@ fn writePaddingPos(w: *Writer, pos: usize) std.Io.Writer.Error!void {
153/// "reasonable system must not assume that such a block exists when reading an155/// "reasonable system must not assume that such a block exists when reading an
154/// archive". Therefore, the Zig standard library recommends to not call this156/// archive". Therefore, the Zig standard library recommends to not call this
155/// function.157/// function.
156pub fn finishPedantically(w: *Writer) std.Io.Writer.Error!void {158pub fn finishPedantically(w: *Writer) Io.Writer.Error!void {
157 try w.underlying_writer.splatByteAll(0, block_size * 2);159 try w.underlying_writer.splatByteAll(0, block_size * 2);
158}160}
159161
...@@ -248,7 +250,7 @@ pub const Header = extern struct {...@@ -248,7 +250,7 @@ pub const Header = extern struct {
248 try octal(&w.checksum, checksum);250 try octal(&w.checksum, checksum);
249 }251 }
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 {
252 try h.updateChecksum();254 try h.updateChecksum();
253 try bw.writeAll(std.mem.asBytes(h));255 try bw.writeAll(std.mem.asBytes(h));
254 }256 }
...@@ -396,14 +398,14 @@ test "write files" {...@@ -396,14 +398,14 @@ test "write files" {
396 {398 {
397 const root = "root";399 const root = "root";
398400
399 var output: std.Io.Writer.Allocating = .init(testing.allocator);401 var output: Io.Writer.Allocating = .init(testing.allocator);
400 var w: Writer = .{ .underlying_writer = &output.writer };402 var w: Writer = .{ .underlying_writer = &output.writer };
401 defer output.deinit();403 defer output.deinit();
402 try w.setRoot(root);404 try w.setRoot(root);
403 for (files) |file|405 for (files) |file|
404 try w.writeFileBytes(file.path, file.content, .{});406 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());
407 var it: std.tar.Iterator = .init(&input, .{409 var it: std.tar.Iterator = .init(&input, .{
408 .file_name_buffer = &file_name_buffer,410 .file_name_buffer = &file_name_buffer,
409 .link_name_buffer = &link_name_buffer,411 .link_name_buffer = &link_name_buffer,
...@@ -424,7 +426,7 @@ test "write files" {...@@ -424,7 +426,7 @@ test "write files" {
424 try testing.expectEqual('/', actual.name[root.len..][0]);426 try testing.expectEqual('/', actual.name[root.len..][0]);
425 try testing.expectEqualStrings(expected.path, actual.name[root.len + 1 ..]);427 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);
428 defer content.deinit();430 defer content.deinit();
429 try it.streamRemaining(actual, &content.writer);431 try it.streamRemaining(actual, &content.writer);
430 try testing.expectEqualSlices(u8, expected.content, content.written());432 try testing.expectEqualSlices(u8, expected.content, content.written());
...@@ -432,15 +434,15 @@ test "write files" {...@@ -432,15 +434,15 @@ test "write files" {
432 }434 }
433 // without root435 // without root
434 {436 {
435 var output: std.Io.Writer.Allocating = .init(testing.allocator);437 var output: Io.Writer.Allocating = .init(testing.allocator);
436 var w: Writer = .{ .underlying_writer = &output.writer };438 var w: Writer = .{ .underlying_writer = &output.writer };
437 defer output.deinit();439 defer output.deinit();
438 for (files) |file| {440 for (files) |file| {
439 var content: std.Io.Reader = .fixed(file.content);441 var content: Io.Reader = .fixed(file.content);
440 try w.writeFileStream(file.path, file.content.len, &content, .{});442 try w.writeFileStream(file.path, file.content.len, &content, .{});
441 }443 }
442444
443 var input: std.Io.Reader = .fixed(output.written());445 var input: Io.Reader = .fixed(output.written());
444 var it: std.tar.Iterator = .init(&input, .{446 var it: std.tar.Iterator = .init(&input, .{
445 .file_name_buffer = &file_name_buffer,447 .file_name_buffer = &file_name_buffer,
446 .link_name_buffer = &link_name_buffer,448 .link_name_buffer = &link_name_buffer,
...@@ -452,7 +454,7 @@ test "write files" {...@@ -452,7 +454,7 @@ test "write files" {
452 const expected = files[i];454 const expected = files[i];
453 try testing.expectEqualStrings(expected.path, actual.name);455 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);
456 defer content.deinit();458 defer content.deinit();
457 try it.streamRemaining(actual, &content.writer);459 try it.streamRemaining(actual, &content.writer);
458 try testing.expectEqualSlices(u8, expected.content, content.written());460 try testing.expectEqualSlices(u8, expected.content, content.written());
lib/std/zig.zig+1-1
...@@ -559,7 +559,7 @@ test isUnderscore {...@@ -559,7 +559,7 @@ test isUnderscore {
559/// If the source can be UTF-16LE encoded, this function asserts that `gpa`559/// If the source can be UTF-16LE encoded, this function asserts that `gpa`
560/// will align a byte-sized allocation to at least 2. Allocators that don't do560/// will align a byte-sized allocation to at least 2. Allocators that don't do
561/// this are rare.561/// 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 {
563 var buffer: std.ArrayList(u8) = .empty;563 var buffer: std.ArrayList(u8) = .empty;
564 defer buffer.deinit(gpa);564 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 {...@@ -442,6 +442,7 @@ pub fn resolveTargetQuery(io: Io, query: Target.Query) DetectError!Target {
442 error.DeviceBusy,442 error.DeviceBusy,
443 error.InputOutput,443 error.InputOutput,
444 error.LockViolation,444 error.LockViolation,
445 error.FileSystem,
445446
446 error.UnableToOpenElfFile,447 error.UnableToOpenElfFile,
447 error.UnhelpfulFile,448 error.UnhelpfulFile,
...@@ -542,16 +543,15 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T...@@ -542,16 +543,15 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T
542 return null;543 return null;
543}544}
544545
545pub const AbiAndDynamicLinkerFromFileError = error{};546fn abiAndDynamicLinkerFromFile(
546
547pub fn abiAndDynamicLinkerFromFile(
548 file_reader: *Io.File.Reader,547 file_reader: *Io.File.Reader,
549 header: *const elf.Header,548 header: *const elf.Header,
550 cpu: Target.Cpu,549 cpu: Target.Cpu,
551 os: Target.Os,550 os: Target.Os,
552 ld_info_list: []const LdInfo,551 ld_info_list: []const LdInfo,
553 query: Target.Query,552 query: Target.Query,
554) AbiAndDynamicLinkerFromFileError!Target {553) !Target {
554 const io = file_reader.io;
555 var result: Target = .{555 var result: Target = .{
556 .cpu = cpu,556 .cpu = cpu,
557 .os = os,557 .os = os,
...@@ -623,8 +623,8 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -623,8 +623,8 @@ pub fn abiAndDynamicLinkerFromFile(
623 try file_reader.seekTo(shstr.sh_offset);623 try file_reader.seekTo(shstr.sh_offset);
624 try file_reader.interface.readSliceAll(shstrtab);624 try file_reader.interface.readSliceAll(shstrtab);
625 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: {625 const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: {
626 var it = header.iterateSectionHeaders(&file_reader.interface);626 var it = header.iterateSectionHeaders(file_reader);
627 while (it.next()) |shdr| {627 while (try it.next()) |shdr| {
628 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;628 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
629 const sh_name = shstrtab[shdr.sh_name..end :0];629 const sh_name = shstrtab[shdr.sh_name..end :0];
630 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{630 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
...@@ -645,7 +645,7 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -645,7 +645,7 @@ pub fn abiAndDynamicLinkerFromFile(
645645
646 var it = mem.tokenizeScalar(u8, rpath_list, ':');646 var it = mem.tokenizeScalar(u8, rpath_list, ':');
647 while (it.next()) |rpath| {647 while (it.next()) |rpath| {
648 if (glibcVerFromRPath(rpath)) |ver| {648 if (glibcVerFromRPath(io, rpath)) |ver| {
649 result.os.version_range.linux.glibc = ver;649 result.os.version_range.linux.glibc = ver;
650 return result;650 return result;
651 } else |err| switch (err) {651 } else |err| switch (err) {
...@@ -660,7 +660,7 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -660,7 +660,7 @@ pub fn abiAndDynamicLinkerFromFile(
660 // There is no DT_RUNPATH so we try to find libc.so.6 inside the same660 // There is no DT_RUNPATH so we try to find libc.so.6 inside the same
661 // directory as the dynamic linker.661 // directory as the dynamic linker.
662 if (fs.path.dirname(dl_path)) |rpath| {662 if (fs.path.dirname(dl_path)) |rpath| {
663 if (glibcVerFromRPath(rpath)) |ver| {663 if (glibcVerFromRPath(io, rpath)) |ver| {
664 result.os.version_range.linux.glibc = ver;664 result.os.version_range.linux.glibc = ver;
665 return result;665 return result;
666 } else |err| switch (err) {666 } else |err| switch (err) {
...@@ -725,7 +725,7 @@ pub fn abiAndDynamicLinkerFromFile(...@@ -725,7 +725,7 @@ pub fn abiAndDynamicLinkerFromFile(
725 @memcpy(path_buf[index..][0..abi.len], abi);725 @memcpy(path_buf[index..][0..abi.len], abi);
726 index += abi.len;726 index += abi.len;
727 const rpath = path_buf[0..index];727 const rpath = path_buf[0..index];
728 if (glibcVerFromRPath(rpath)) |ver| {728 if (glibcVerFromRPath(io, rpath)) |ver| {
729 result.os.version_range.linux.glibc = ver;729 result.os.version_range.linux.glibc = ver;
730 return result;730 return result;
731 } else |err| switch (err) {731 } else |err| switch (err) {
...@@ -842,18 +842,13 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {...@@ -842,18 +842,13 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
842 error.InvalidElfMagic,842 error.InvalidElfMagic,
843 error.InvalidElfEndian,843 error.InvalidElfEndian,
844 error.InvalidElfClass,844 error.InvalidElfClass,
845 error.InvalidElfFile,
846 error.InvalidElfVersion,845 error.InvalidElfVersion,
847 error.InvalidGnuLibCVersion,846 error.InvalidGnuLibCVersion,
848 error.EndOfStream,847 error.EndOfStream,
849 => return error.GLibCNotFound,848 => return error.GLibCNotFound,
850849
851 error.SystemResources,850 error.ReadFailed => return file_reader.err.?,
852 error.UnableToReadElfFile,851 else => |e| return e,
853 error.Unexpected,
854 error.FileSystem,
855 error.ProcessNotFound,
856 => |e| return e,
857 };852 };
858}853}
859854
...@@ -867,8 +862,8 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {...@@ -867,8 +862,8 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
867 try file_reader.seekTo(shstr.sh_offset);862 try file_reader.seekTo(shstr.sh_offset);
868 try file_reader.interface.readSliceAll(shstrtab);863 try file_reader.interface.readSliceAll(shstrtab);
869 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: {864 const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: {
870 var it = header.iterateSectionHeaders(&file_reader.interface);865 var it = header.iterateSectionHeaders(file_reader);
871 while (it.next()) |shdr| {866 while (try it.next()) |shdr| {
872 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;867 const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue;
873 const sh_name = shstrtab[shdr.sh_name..end :0];868 const sh_name = shstrtab[shdr.sh_name..end :0];
874 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{869 if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{
...@@ -882,19 +877,25 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {...@@ -882,19 +877,25 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion {
882 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,877 // strings that start with "GLIBC_2." indicate the existence of such a glibc version,
883 // and furthermore, that the system-installed glibc is at minimum that version.878 // and furthermore, that the system-installed glibc is at minimum that version.
884 var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };879 var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 };
885880 var offset: u64 = 0;
886 try file_reader.seekTo(dynstr.offset);881 try file_reader.seekTo(dynstr.offset);
887 while (file_reader.interface.takeSentinel(0)) |s| {882 while (offset < dynstr.size) {
888 if (mem.startsWith(u8, s, "GLIBC_2.")) {883 if (file_reader.interface.takeSentinel(0)) |s| {
889 const chopped = s["GLIBC_".len..];884 if (mem.startsWith(u8, s, "GLIBC_2.")) {
890 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {885 const chopped = s["GLIBC_".len..];
891 error.Overflow => return error.InvalidGnuLibCVersion,886 const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) {
892 error.InvalidVersion => return error.InvalidGnuLibCVersion,887 error.Overflow => return error.InvalidGnuLibCVersion,
893 };888 error.InvalidVersion => return error.InvalidGnuLibCVersion,
894 switch (ver.order(max_ver)) {889 };
895 .gt => max_ver = ver,890 switch (ver.order(max_ver)) {
896 .lt, .eq => continue,891 .gt => max_ver = ver,
892 .lt, .eq => continue,
893 }
897 }894 }
895 offset += s.len + 1;
896 } else |err| switch (err) {
897 error.EndOfStream, error.StreamTooLong => break,
898 error.ReadFailed => |e| return e,
898 }899 }
899 }900 }
900901
...@@ -1091,22 +1092,12 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ...@@ -1091,22 +1092,12 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
1091 error.ProcessFdQuotaExceeded,1092 error.ProcessFdQuotaExceeded,
1092 error.SystemFdQuotaExceeded,1093 error.SystemFdQuotaExceeded,
1093 error.ProcessNotFound,1094 error.ProcessNotFound,
1095 error.Canceled,
1094 => |e| return e,1096 => |e| return e,
10951097
1096 error.ReadFailed => return file_reader.err.?,1098 error.ReadFailed => return file_reader.err.?,
10971099
1098 error.UnableToReadElfFile,1100 else => |e| {
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| {
1110 std.log.warn("encountered {t}; falling back to default ABI and dynamic linker", .{e});1101 std.log.warn("encountered {t}; falling back to default ABI and dynamic linker", .{e});
1111 return defaultAbiAndDynamicLinker(cpu, os, query);1102 return defaultAbiAndDynamicLinker(cpu, os, query);
1112 },1103 },
test/src/Cases.zig+2-11
...@@ -455,8 +455,7 @@ pub fn lowerToBuildSteps(...@@ -455,8 +455,7 @@ pub fn lowerToBuildSteps(
455 parent_step: *std.Build.Step,455 parent_step: *std.Build.Step,
456 options: CaseTestOptions,456 options: CaseTestOptions,
457) void {457) void {
458 const host = std.zig.system.resolveTargetQuery(.{}) catch |err|458 const host = b.resolveTargetQuery(.{});
459 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});
460 const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");459 const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");
461460
462 for (self.cases.items) |case| {461 for (self.cases.items) |case| {
...@@ -587,7 +586,7 @@ pub fn lowerToBuildSteps(...@@ -587,7 +586,7 @@ pub fn lowerToBuildSteps(
587 },586 },
588 .Execution => |expected_stdout| no_exec: {587 .Execution => |expected_stdout| no_exec: {
589 const run = if (case.target.result.ofmt == .c) run_step: {588 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) {
591 // We wouldn't be able to run the compiled C code.590 // We wouldn't be able to run the compiled C code.
592 break :no_exec;591 break :no_exec;
593 }592 }
...@@ -972,14 +971,6 @@ const TestManifest = struct {...@@ -972,14 +971,6 @@ const TestManifest = struct {
972 }971 }
973};972};
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
983fn knownFileExtension(filename: []const u8) bool {974fn knownFileExtension(filename: []const u8) bool {
984 // List taken from `Compilation.classifyFileExt` in the compiler.975 // List taken from `Compilation.classifyFileExt` in the compiler.
985 for ([_][]const u8{976 for ([_][]const u8{