| author | |
| committer | |
| log | 47aa5a70a54ef7838e7c8e5ebdc570f07048ec04 |
| tree | 80c2b9edc39f3b51746e89b7c90a1eab79075ff5 |
| parent | 066864a0bf59bc1a926412b3c6e4d2d0c65e5642 |
got the build runner compiling34 files changed, 805 insertions(+), 564 deletions(-)
lib/compiler/build_runner.zig+18-14| ... | @@ -1,5 +1,8 @@ | ... | @@ -1,5 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const runner = @This(); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | |||
| 4 | const std = @import("std"); | ||
| 5 | const Io = std.Io; | ||
| 3 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 4 | const fmt = std.fmt; | 7 | const fmt = std.fmt; |
| 5 | const mem = std.mem; | 8 | const mem = std.mem; |
| ... | @@ -11,7 +14,6 @@ const WebServer = std.Build.WebServer; | ... | @@ -11,7 +14,6 @@ const WebServer = std.Build.WebServer; |
| 11 | const Allocator = std.mem.Allocator; | 14 | const Allocator = std.mem.Allocator; |
| 12 | const fatal = std.process.fatal; | 15 | const fatal = std.process.fatal; |
| 13 | const Writer = std.Io.Writer; | 16 | const Writer = std.Io.Writer; |
| 14 | const runner = @This(); | ||
| 15 | const tty = std.Io.tty; | 17 | const tty = std.Io.tty; |
| 16 | 18 | ||
| 17 | pub const root = @import("@build"); | 19 | pub 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; |
| 125 | 128 | ||
| 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 { |
| 550 | 549 | ||
| 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 | }; |
| 556 | 555 | ||
| 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(); |
| 559 | 558 | ||
| 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` above | 562 | 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; |
| 573 | 575 | ||
| 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 | } |
| 577 | 579 | ||
| 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; |
| 760 | 763 | ||
| ... | @@ -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 @@ |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | 3 | ||
| 4 | const std = @import("std"); | 4 | const std = @import("std"); |
| 5 | const Io = std.Io; | ||
| 5 | const fatal = std.process.fatal; | 6 | const fatal = std.process.fatal; |
| 6 | const testing = std.testing; | 7 | const testing = std.testing; |
| 7 | const assert = std.debug.assert; | 8 | const 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); |
| 16 | var fba_buffer: [8192]u8 = undefined; | 17 | var fba_buffer: [8192]u8 = undefined; |
| 17 | var stdin_buffer: [4096]u8 = undefined; | 18 | var stdin_buffer: [4096]u8 = undefined; |
| 18 | var stdout_buffer: [4096]u8 = undefined; | 19 | var stdout_buffer: [4096]u8 = undefined; |
| 20 | var runner_threaded_io: Io.Threaded = .init_single_threaded; | ||
| 19 | 21 | ||
| 20 | /// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether | 22 | /// 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 | } |
| 65 | 67 | ||
| 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 { |
| 74 | 74 | ||
| 75 | fn mainServer() !void { | 75 | fn 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 { |
| 131 | 131 | ||
| 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; |
| 243 | 242 | ||
| ... | @@ -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; |
| 1839 | 1839 | ||
| 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(); |
| 1849 | 1851 | ||
| 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. |
| 4 | 4 | ||
| 5 | const Cache = @This(); | 5 | const Cache = @This(); |
| 6 | const std = @import("std"); | ||
| 7 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | |||
| 8 | const std = @import("std"); | ||
| 9 | const Io = std.Io; | ||
| 8 | const crypto = std.crypto; | 10 | const crypto = std.crypto; |
| 9 | const fs = std.fs; | 11 | const fs = std.fs; |
| 10 | const assert = std.debug.assert; | 12 | const assert = std.debug.assert; |
| ... | @@ -15,6 +17,7 @@ const Allocator = std.mem.Allocator; | ... | @@ -15,6 +17,7 @@ const Allocator = std.mem.Allocator; |
| 15 | const log = std.log.scoped(.cache); | 17 | const log = std.log.scoped(.cache); |
| 16 | 18 | ||
| 17 | gpa: Allocator, | 19 | gpa: Allocator, |
| 20 | io: Io, | ||
| 18 | manifest_dir: fs.Dir, | 21 | manifest_dir: fs.Dir, |
| 19 | hash: HashHelper = .{}, | 22 | hash: 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 exceeded | 669 | 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; |
| 1338 | 1342 | ||
| 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; |
| 1403 | 1408 | ||
| 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 | } |
| 1452 | 1458 | ||
| 1453 | test "no file inputs" { | 1459 | test "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(); |
| 1456 | 1464 | ||
| ... | @@ -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; |
| 1461 | 1469 | ||
| 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; |
| 1518 | 1527 | ||
| 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 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const Io = std.Io; | ||
| 2 | const Build = std.Build; | 3 | const Build = std.Build; |
| 3 | const Cache = Build.Cache; | 4 | const Cache = Build.Cache; |
| 4 | const Step = std.Build.Step; | 5 | const Step = std.Build.Step; |
| ... | @@ -14,6 +15,7 @@ const Fuzz = @This(); | ... | @@ -14,6 +15,7 @@ const Fuzz = @This(); |
| 14 | const build_runner = @import("root"); | 15 | const build_runner = @import("root"); |
| 15 | 16 | ||
| 16 | gpa: Allocator, | 17 | gpa: Allocator, |
| 18 | io: Io, | ||
| 17 | mode: Mode, | 19 | mode: Mode, |
| 18 | 20 | ||
| 19 | /// Allocated into `gpa`. | 21 | /// Allocated into `gpa`. |
| ... | @@ -75,6 +77,7 @@ const CoverageMap = struct { | ... | @@ -75,6 +77,7 @@ const CoverageMap = struct { |
| 75 | 77 | ||
| 76 | pub fn init( | 78 | pub 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( |
| 111 | 114 | ||
| 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 |
| 484 | 488 | ||
| 485 | pub fn waitAndPrintReport(fuzz: *Fuzz) void { | 489 | pub fn waitAndPrintReport(fuzz: *Fuzz) void { |
| 486 | assert(fuzz.mode == .limit); | 490 | assert(fuzz.mode == .limit); |
| 491 | const io = fuzz.io; | ||
| 487 | 492 | ||
| 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 { |
| 506 | 511 | ||
| 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); |
| 510 | 515 | ||
| 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 @@ |
| 1 | const Step = @This(); | 1 | const Step = @This(); |
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 2 | const std = @import("../std.zig"); | 4 | const std = @import("../std.zig"); |
| 5 | const Io = std.Io; | ||
| 3 | const Build = std.Build; | 6 | const Build = std.Build; |
| 4 | const Allocator = std.mem.Allocator; | 7 | const Allocator = std.mem.Allocator; |
| 5 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 6 | const builtin = @import("builtin"); | ||
| 7 | const Cache = Build.Cache; | 9 | const Cache = Build.Cache; |
| 8 | const Path = Cache.Path; | 10 | const Path = Cache.Path; |
| 9 | const ArrayList = std.ArrayList; | 11 | const 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 | } |
| 328 | 330 | ||
| 329 | /// For debugging purposes, prints identifying information about this Step. | 331 | /// For debugging purposes, prints identifying information about this Step. |
| 330 | pub fn dump(step: *Step, w: *std.Io.Writer, tty_config: std.Io.tty.Config) void { | 332 | pub 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 |
| 382 | 384 | ||
| 383 | pub const ZigProcess = struct { | 385 | pub 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, |
| 387 | 389 | ||
| 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 | } |
| 506 | 508 | ||
| 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. |
| 508 | pub fn installFile(s: *Step, src_lazy_path: Build.LazyPath, dest_path: []const u8) !std.fs.Dir.PrevStatus { | 510 | pub 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 | }; |
| 774 | 777 | ||
| 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(); |
| 539 | 539 | ||
| 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 | }; |
| 762 | fn make(step: *Step, options: Step.MakeOptions) !void { | 762 | fn 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(); |
| 835 | 836 | ||
| 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(); |
| 1094 | 1096 | ||
| 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( |
| 2090 | 2092 | ||
| 2091 | fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { | 2093 | fn 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; |
| 2094 | 2097 | ||
| 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/23955 | 2117 | // 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 a | 3 | //! 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 be | 4 | //! developer with intention to update source files, which will then be |
| 5 | //! committed to version control. | 5 | //! committed to version control. |
| 6 | const UpdateSourceFiles = @This(); | ||
| 7 | |||
| 6 | const std = @import("std"); | 8 | const std = @import("std"); |
| 9 | const Io = std.Io; | ||
| 7 | const Step = std.Build.Step; | 10 | const Step = std.Build.Step; |
| 8 | const fs = std.fs; | 11 | const fs = std.fs; |
| 9 | const ArrayList = std.ArrayList; | 12 | const ArrayList = std.ArrayList; |
| 10 | const UpdateSourceFiles = @This(); | ||
| 11 | 13 | ||
| 12 | step: Step, | 14 | step: Step, |
| 13 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), | 15 | output_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: [] |
| 70 | fn make(step: *Step, options: Step.MakeOptions) !void { | 72 | fn 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); |
| 74 | 77 | ||
| 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); |
| 95 | 96 | ||
| 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 generated | 2 | //! 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. |
| 4 | const std = @import("std"); | 4 | const std = @import("std"); |
| 5 | const Io = std.Io; | ||
| 5 | const Step = std.Build.Step; | 6 | const Step = std.Build.Step; |
| 6 | const fs = std.fs; | 7 | const fs = std.fs; |
| 7 | const ArrayList = std.ArrayList; | 8 | const ArrayList = std.ArrayList; |
| ... | @@ -174,6 +175,7 @@ fn maybeUpdateName(write_file: *WriteFile) void { | ... | @@ -174,6 +175,7 @@ fn maybeUpdateName(write_file: *WriteFile) void { |
| 174 | fn make(step: *Step, options: Step.MakeOptions) !void { | 175 | fn 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(); |
| 266 | 268 | ||
| 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, |
| 3 | graph: *const Build.Graph, | 3 | graph: *const Build.Graph, |
| 4 | all_steps: []const *Build.Step, | 4 | all_steps: []const *Build.Step, |
| 5 | listen_address: net.IpAddress, | 5 | listen_address: net.IpAddress, |
| 6 | ttyconf: std.Io.tty.Config, | 6 | ttyconf: Io.tty.Config, |
| 7 | root_prog_node: std.Progress.Node, | 7 | root_prog_node: std.Progress.Node, |
| 8 | watch: bool, | 8 | watch: bool, |
| 9 | 9 | ||
| 10 | tcp_server: ?net.Server, | 10 | tcp_server: ?net.Server, |
| 11 | serve_thread: ?std.Thread, | 11 | serve_thread: ?std.Thread, |
| 12 | 12 | ||
| 13 | base_timestamp: i128, | 13 | /// Uses `Io.Clock.awake`. |
| 14 | base_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`. |
| 15 | step_names_trailing: []u8, | 16 | step_names_trailing: []u8, |
| 16 | 17 | ||
| ... | @@ -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 | }; |
| 61 | pub fn init(opts: Options) WebServer { | 63 | pub 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); | ||
| 65 | 68 | ||
| 66 | const all_steps = opts.all_steps; | 69 | const all_steps = opts.all_steps; |
| 67 | 70 | ||
| ... | @@ -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, |
| 108 | 111 | ||
| 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, |
| 111 | 114 | ||
| 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 { |
| 147 | pub fn start(ws: *WebServer) error{AlreadyReported}!void { | 150 | pub 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; | ||
| 150 | 154 | ||
| 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 | }; |
| 161 | 165 | ||
| 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 | } |
| 167 | fn serve(ws: *WebServer) void { | 171 | fn 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 { |
| 227 | 232 | ||
| 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 | } |
| 242 | 248 | ||
| 243 | pub fn now(s: *const WebServer) i64 { | 249 | pub 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 | } |
| 246 | 255 | ||
| 247 | fn accept(ws: *WebServer, connection: net.Server.Connection) void { | 256 | fn accept(ws: *WebServer, stream: net.Stream) void { |
| 248 | defer connection.stream.close(); | 257 | const io = ws.graph.io; |
| 249 | 258 | defer { | |
| 259 | // `net.Stream.close` wants to helpfully overwrite `stream` with | ||
| 260 | // `undefined`, but it cannot do so since it is an immutable parameter. | ||
| 261 | var copy = stream; | ||
| 262 | copy.close(io); | ||
| 263 | } | ||
| 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); |
| 255 | 269 | ||
| 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 | } |
| 469 | pub fn serveTarFile( | 483 | pub 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; | ||
| 475 | 486 | ||
| 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); |
| 500 | 511 | ||
| 501 | // TODO: this logic is completely bogus -- obviously so, because `path.root_dir.path` can | 512 | // 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(); |
| 568 | 579 | ||
| 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 | }; |
| 843 | 854 | ||
| 844 | const builtin = @import("builtin"); | 855 | const builtin = @import("builtin"); |
| 856 | |||
| 845 | const std = @import("std"); | 857 | const std = @import("std"); |
| 858 | const Io = std.Io; | ||
| 859 | const net = std.Io.net; | ||
| 846 | const assert = std.debug.assert; | 860 | const assert = std.debug.assert; |
| 847 | const mem = std.mem; | 861 | const mem = std.mem; |
| 848 | const log = std.log.scoped(.web_server); | 862 | const log = std.log.scoped(.web_server); |
| ... | @@ -852,6 +866,5 @@ const Cache = Build.Cache; | ... | @@ -852,6 +866,5 @@ const Cache = Build.Cache; |
| 852 | const Fuzz = Build.Fuzz; | 866 | const Fuzz = Build.Fuzz; |
| 853 | const abi = Build.abi; | 867 | const abi = Build.abi; |
| 854 | const http = std.http; | 868 | const http = std.http; |
| 855 | const net = std.Io.net; | ||
| 856 | 869 | ||
| 857 | const WebServer = @This(); | 870 | const 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, |
| 656 | 656 | ||
| 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 | }; |
| 808 | 816 | ||
| 809 | pub const Duration = struct { | 817 | pub 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 | } |
| 833 | 841 | ||
| 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; |
| 6 | 6 | ||
| 7 | handle: Handle, | 7 | handle: Handle, |
| 8 | 8 | ||
| 9 | pub const Mode = Io.File.Mode; | ||
| 10 | pub const default_mode: Mode = 0o755; | ||
| 11 | |||
| 9 | pub fn cwd() Dir { | 12 | pub 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; |
| 47 | 50 | ||
| 48 | /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If | 51 | /// 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` to | 52 | /// they are equal, does nothing. Otherwise, atomically copies `source_path` to |
| 50 | /// `dest_path`. The destination file gains the mtime, atime, and mode of the | 53 | /// `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); |
| 69 | 73 | ||
| 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 | } |
| 94 | 98 | ||
| 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(); |
| 101 | 105 | ||
| 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; |
| 104 | 108 | ||
| 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 | |||
| 119 | pub 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. | ||
| 131 | pub 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 | |||
| 143 | pub 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` | ||
| 178 | pub 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 | |||
| 182 | pub 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. | ||
| 206 | pub fn makePath(dir: Dir, io: Io, sub_path: []const u8) MakePathError!void { | ||
| 207 | _ = try makePathStatus(dir, io, sub_path); | ||
| 208 | } | ||
| 209 | |||
| 210 | pub const MakePathStatus = enum { existed, created }; | ||
| 211 | |||
| 212 | /// Same as `makePath` except returns whether the path already existed or was | ||
| 213 | /// successfully created. | ||
| 214 | pub 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 | |||
| 245 | pub const Stat = File.Stat; | ||
| 246 | pub const StatError = File.StatError; | ||
| 247 | |||
| 248 | pub fn stat(dir: Dir, io: Io) StatError!Stat { | ||
| 249 | return io.vtable.dirStat(io.userdata, dir); | ||
| 250 | } | ||
| 251 | |||
| 252 | pub 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. | ||
| 266 | pub 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 { |
| 446 | 446 | ||
| 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 { |
| 63 | 63 | ||
| 64 | pub const InitError = std.Thread.CpuCountError || Allocator.Error; | 64 | pub const InitError = std.Thread.CpuCountError || Allocator.Error; |
| 65 | 65 | ||
| 66 | pub fn init(gpa: Allocator) Pool { | 66 | /// Related: |
| 67 | /// * `init_single_threaded` | ||
| 68 | pub 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 | } |
| 79 | 89 | ||
| 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. | ||
| 96 | pub 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 | |||
| 80 | pub fn deinit(pool: *Pool) void { | 104 | pub 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, |
| 138 | 162 | ||
| 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( |
| 520 | 548 | ||
| 521 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { | 549 | fn 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; |
| 524 | 552 | ||
| 525 | if (builtin.single_threaded) return; | 553 | if (builtin.single_threaded) return; |
| 526 | 554 | ||
| 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 | } |
| 538 | 568 | ||
| ... | @@ -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 | } |
| 726 | 756 | ||
| 757 | fn 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 | |||
| 767 | fn 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 | |||
| 775 | fn 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 | |||
| 784 | fn 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 | |||
| 727 | fn createFile( | 792 | fn 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 | }; |
| 2828 | 2828 | ||
| 2829 | test "discarding sendFile" { | 2829 | test "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(); |
| 2832 | 2834 | ||
| ... | @@ -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(); |
| 2839 | 2841 | ||
| 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); |
| 2842 | 2844 | ||
| 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 | } |
| 2848 | 2850 | ||
| 2849 | test "allocating sendFile" { | 2851 | test "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(); |
| 2852 | 2856 | ||
| ... | @@ -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(); |
| 2859 | 2863 | ||
| 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); |
| 2863 | 2867 |
lib/std/Io/net.zig+33| ... | @@ -57,6 +57,39 @@ pub const IpAddress = union(enum) { | ... | @@ -57,6 +57,39 @@ pub const IpAddress = union(enum) { |
| 57 | 57 | ||
| 58 | pub const Family = @typeInfo(IpAddress).@"union".tag_type.?; | 58 | pub const Family = @typeInfo(IpAddress).@"union".tag_type.?; |
| 59 | 59 | ||
| 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 have | 95 | /// 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; | ||
| 81 | 83 | ||
| 82 | pub const LookupResult = struct { | 84 | pub 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, |
| 430 | 432 | ||
| 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); |
| 434 | 441 | ||
| 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 | } |
| 441 | 454 |
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(); |
| 213 | 213 | ||
| 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); |
| 219 | 219 | ||
| 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 { |
| 267 | 267 | ||
| 268 | const io = testing.io; | 268 | const io = testing.io; |
| 269 | 269 | ||
| 270 | var client = try server.accept(io); | 270 | var stream = try server.accept(io); |
| 271 | 271 | 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 | } |
| 275 | 274 | ||
| 276 | test "listen on a unix socket, send bytes, receive bytes" { | 275 | 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,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(); |
| 312 | 311 | ||
| 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); |
| 318 | 317 | ||
| 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(); |
| 368 | 367 | ||
| 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", .{}); |
| 373 | 372 | ||
| 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 allows | 116 | /// 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 equals | 128 | /// 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); |
| 191 | 197 | ||
| 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].*; |
| 198 | 202 | ||
| 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; |
| 325 | 329 | ||
| 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(); |
| 436 | 436 | ||
| 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)); |
| 439 | 439 | ||
| 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 | } |
| 545 | 545 | ||
| 546 | const std = @import("std"); | 546 | const std = @import("std"); |
| 547 | const Io = std.Io; | ||
| 547 | const Allocator = std.mem.Allocator; | 548 | const Allocator = std.mem.Allocator; |
| 548 | const Dwarf = std.debug.Dwarf; | 549 | const Dwarf = std.debug.Dwarf; |
| 549 | const Pdb = std.debug.Pdb; | 550 | const 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); |
| 712 | 712 | ||
| 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 | }; |
| 716 | 716 | ||
| ... | @@ -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..]); |
| 733 | 733 | ||
| 734 | return takeProgramHeader(&reader, it.is_64, it.endian); | 734 | return try takeProgramHeader(&reader, it.is_64, it.endian); |
| 735 | } | 735 | } |
| 736 | }; | 736 | }; |
| 737 | 737 | ||
| ... | @@ -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); |
| 773 | 773 | ||
| 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 | }; |
| 777 | 777 | ||
| ... | @@ -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)..]); |
| 795 | 795 | ||
| 796 | return takeSectionHeader(&reader, it.is_64, it.endian); | 796 | return try takeSectionHeader(&reader, it.is_64, it.endian); |
| 797 | } | 797 | } |
| 798 | }; | 798 | }; |
| 799 | 799 | ||
| ... | @@ -826,12 +826,12 @@ pub const DynamicSectionIterator = struct { | ... | @@ -826,12 +826,12 @@ pub const DynamicSectionIterator = struct { |
| 826 | 826 | ||
| 827 | file_reader: *Io.File.Reader, | 827 | file_reader: *Io.File.Reader, |
| 828 | 828 | ||
| 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 | }; |
| 837 | 837 |
lib/std/fs/Dir.zig+56-94| ... | @@ -1,6 +1,11 @@ | ... | @@ -1,6 +1,11 @@ |
| 1 | //! Deprecated in favor of `Io.Dir`. | ||
| 1 | const Dir = @This(); | 2 | const Dir = @This(); |
| 3 | |||
| 2 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 5 | const native_os = builtin.os.tag; | ||
| 6 | |||
| 3 | const std = @import("../std.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const Io = std.Io; | ||
| 4 | const File = std.fs.File; | 9 | const File = std.fs.File; |
| 5 | const AtomicFile = std.fs.AtomicFile; | 10 | const AtomicFile = std.fs.AtomicFile; |
| 6 | const base64_encoder = fs.base64_encoder; | 11 | const base64_encoder = fs.base64_encoder; |
| ... | @@ -12,7 +17,6 @@ const Allocator = std.mem.Allocator; | ... | @@ -12,7 +17,6 @@ const Allocator = std.mem.Allocator; |
| 12 | const assert = std.debug.assert; | 17 | const assert = std.debug.assert; |
| 13 | const linux = std.os.linux; | 18 | const linux = std.os.linux; |
| 14 | const windows = std.os.windows; | 19 | const windows = std.os.windows; |
| 15 | const native_os = builtin.os.tag; | ||
| 16 | const have_flock = @TypeOf(posix.system.flock) != void; | 20 | const have_flock = @TypeOf(posix.system.flock) != void; |
| 17 | 21 | ||
| 18 | fd: Handle, | 22 | fd: 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 | } |
| 1191 | 1195 | ||
| 1192 | pub const MakeError = posix.MakeDirError; | 1196 | /// Deprecated in favor of `Io.Dir.MakeError`. |
| 1197 | pub const MakeError = Io.Dir.MakeError; | ||
| 1193 | 1198 | ||
| 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. | ||
| 1200 | pub fn makeDir(self: Dir, sub_path: []const u8) MakeError!void { | 1200 | pub 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 | } |
| 1203 | 1205 | ||
| 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`. | ||
| 1207 | pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) MakeError!void { | 1207 | pub 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 | } |
| 1210 | 1210 | ||
| 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`. | ||
| 1214 | pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void { | 1212 | pub 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 | } |
| 1217 | 1215 | ||
| 1218 | /// Calls makeDir iteratively to make an entire path | 1216 | /// Deprecated in favor of `Io.Dir.makePath`. |
| 1219 | /// (i.e. creating any parent directories that do not exist). | 1217 | pub 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. | ||
| 1235 | pub 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 | } |
| 1238 | 1220 | ||
| 1239 | pub 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. | 1222 | pub const MakePathStatus = Io.Dir.MakePathStatus; |
| 1241 | pub 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); | 1224 | pub 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) { | 1227 | pub 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 | } |
| 1271 | 1232 | ||
| 1272 | /// Windows only. Calls makeOpenDirAccessMaskW iteratively to make an entire path | 1233 | /// 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 | } |
| 2054 | 2015 | ||
| 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. | ||
| 2063 | pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 { | 2017 | pub 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(); |
| 2066 | 2020 | 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 | } |
| 2070 | 2022 | ||
| 2071 | pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{ | 2023 | pub 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 a | 2054 | /// 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 | ||
| 2104 | pub fn readFileAllocOptions( | 2058 | pub 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 be | 2604 | /// [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 opaque | 2605 | /// 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 | ||
| 2650 | pub fn copyFile( | 2609 | pub 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); | ||
| 2659 | 2622 | ||
| 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) |
| 2708 | pub const Stat = File.Stat; | 2671 | pub const Stat = File.Stat; |
| 2709 | pub const StatError = File.StatError; | 2672 | pub const StatError = File.StatError; |
| 2710 | 2673 | ||
| 2674 | /// Deprecated in favor of `Io.Dir.stat`. | ||
| 2711 | pub fn stat(self: Dir) StatError!Stat { | 2675 | pub 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 { |
| 2715 | 2679 | ||
| 2716 | pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError; | 2680 | pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError; |
| 2717 | 2681 | ||
| 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. | ||
| 2729 | pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat { | 2683 | pub 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 | |||
| 2757 | pub fn adaptToNewApi(dir: Dir) Io.Dir { | ||
| 2758 | return .{ .handle = dir.fd }; | ||
| 2759 | } | ||
| 2760 | |||
| 2761 | pub 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 | } |
| 860 | 860 | ||
| 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 not | 1353 | /// Positional is more threadsafe, since the global seek position is not |
| 1352 | /// affected. | 1354 | /// affected. |
| 1353 | pub fn reader(file: File, buffer: []u8) Reader { | 1355 | pub 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 | } |
| 1356 | 1358 | ||
| 1357 | /// Positional is more threadsafe, since the global seek position is not | 1359 | /// Positional is more threadsafe, since the global seek position is not |
| 1358 | /// affected, but when such syscalls are not available, preemptively | 1360 | /// 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. |
| 1360 | pub fn readerStreaming(file: File, buffer: []u8) Reader { | 1362 | pub 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 | } |
| 1363 | 1365 | ||
| 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 | |||
| 1544 | pub fn adaptToNewApi(file: File) std.Io.File { | ||
| 1545 | return .{ .handle = file.handle }; | ||
| 1546 | } | ||
| 1547 | |||
| 1548 | pub 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 @@ |
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const native_os = builtin.os.tag; | ||
| 3 | |||
| 4 | const std = @import("../std.zig"); | ||
| 5 | const Io = std.Io; | ||
| 3 | const testing = std.testing; | 6 | const testing = std.testing; |
| 4 | const fs = std.fs; | 7 | const fs = std.fs; |
| 5 | const mem = std.mem; | 8 | const mem = std.mem; |
| 6 | const wasi = std.os.wasi; | 9 | const wasi = std.os.wasi; |
| 7 | const native_os = builtin.os.tag; | ||
| 8 | const windows = std.os.windows; | 10 | const windows = std.os.windows; |
| 9 | const posix = std.posix; | 11 | const posix = std.posix; |
| 10 | 12 | ||
| ... | @@ -73,6 +75,7 @@ const PathType = enum { | ... | @@ -73,6 +75,7 @@ const PathType = enum { |
| 73 | }; | 75 | }; |
| 74 | 76 | ||
| 75 | const TestContext = struct { | 77 | const 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 | } |
| 1320 | 1324 | ||
| 1321 | test "writev, readv" { | 1325 | test "writev, readv" { |
| 1326 | const io = testing.io; | ||
| 1327 | |||
| 1322 | var tmp = tmpDir(.{}); | 1328 | var tmp = tmpDir(.{}); |
| 1323 | defer tmp.cleanup(); | 1329 | defer tmp.cleanup(); |
| 1324 | 1330 | ||
| ... | @@ -1327,78 +1333,55 @@ test "writev, readv" { | ... | @@ -1327,78 +1333,55 @@ test "writev, readv" { |
| 1327 | 1333 | ||
| 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 | }; | ||
| 1350 | 1338 | ||
| 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(); |
| 1353 | 1341 | ||
| 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 | } |
| 1362 | 1355 | ||
| 1363 | test "pwritev, preadv" { | 1356 | test "pwritev, preadv" { |
| 1357 | const io = testing.io; | ||
| 1358 | |||
| 1364 | var tmp = tmpDir(.{}); | 1359 | var tmp = tmpDir(.{}); |
| 1365 | defer tmp.cleanup(); | 1360 | defer tmp.cleanup(); |
| 1366 | 1361 | ||
| 1367 | const line1 = "line1\n"; | 1362 | const line1 = "line1\n"; |
| 1368 | const line2 = "line2\n"; | 1363 | const line2 = "line2\n"; |
| 1369 | 1364 | 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 | }; | ||
| 1392 | 1368 | ||
| 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(); |
| 1395 | 1371 | ||
| 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 | } |
| 1403 | 1386 | ||
| 1404 | test "setEndPos" { | 1387 | test "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/23806 | 1390 | if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23806 |
| 1408 | 1391 | ||
| 1392 | const io = testing.io; | ||
| 1393 | |||
| 1409 | var tmp = tmpDir(.{}); | 1394 | var tmp = tmpDir(.{}); |
| 1410 | defer tmp.cleanup(); | 1395 | defer tmp.cleanup(); |
| 1411 | 1396 | ||
| ... | @@ -1416,11 +1401,13 @@ test "setEndPos" { | ... | @@ -1416,11 +1401,13 @@ test "setEndPos" { |
| 1416 | 1401 | ||
| 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, &.{}); | ||
| 1419 | 1405 | ||
| 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 | } |
| 1426 | 1413 | ||
| ... | @@ -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 | } |
| 1434 | 1422 | ||
| ... | @@ -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 | } |
| 1442 | 1431 | ||
| 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)); | ||
| 1446 | 1436 | ||
| 1447 | // Invalid file length should error gracefully. Actual limit is host | 1437 | // Invalid file length should error gracefully. Actual limit is host |
| 1448 | // and file-system dependent, but 1PB should fail on filesystems like | 1438 | // 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 | } |
| 1562 | 1546 | ||
| 1563 | test "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 | |||
| 1588 | test "copyFile" { | 1547 | test "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 | }; |
| 1710 | 1669 | ||
| 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; |
| 1713 | 1672 | ||
| 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" { |
| 1944 | 1903 | ||
| 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); |
| 1967 | 1927 | ||
| 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); |
| 1971 | 1932 | ||
| 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); |
| 2007 | 1968 | ||
| 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 | } |
| 2017 | 1971 | ||
| ... | @@ -2079,6 +2033,7 @@ test "invalid UTF-8/WTF-8 paths" { | ... | @@ -2079,6 +2033,7 @@ test "invalid UTF-8/WTF-8 paths" { |
| 2079 | 2033 | ||
| 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 byte | 2037 | // 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"); |
| 2084 | 2039 | ||
| ... | @@ -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, .{})); |
| 2131 | 2086 | ||
| 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, .{})); |
| 2134 | 2090 | ||
| 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)); |
| 2145 | 2101 | ||
| 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 | } |
| 2176 | 2131 | ||
| 2177 | test "read file non vectored" { | 2132 | test "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(); |
| 2180 | 2137 | ||
| ... | @@ -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 | } |
| 2190 | 2147 | ||
| 2191 | var file_reader: std.fs.File.Reader = .init(file, &.{}); | 2148 | var file_reader: std.Io.File.Reader = .initAdapted(file, io, &.{}); |
| 2192 | 2149 | ||
| 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 | } |
| 2206 | 2163 | ||
| 2207 | test "seek keeping partial buffer" { | 2164 | test "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(); |
| 2210 | 2169 | ||
| ... | @@ -2219,7 +2178,7 @@ test "seek keeping partial buffer" { | ... | @@ -2219,7 +2178,7 @@ test "seek keeping partial buffer" { |
| 2219 | } | 2178 | } |
| 2220 | 2179 | ||
| 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); |
| 2223 | 2182 | ||
| 2224 | try testing.expectEqual(0, file_reader.logicalPos()); | 2183 | try testing.expectEqual(0, file_reader.logicalPos()); |
| 2225 | 2184 | ||
| ... | @@ -2246,13 +2205,15 @@ test "seek keeping partial buffer" { | ... | @@ -2246,13 +2205,15 @@ test "seek keeping partial buffer" { |
| 2246 | } | 2205 | } |
| 2247 | 2206 | ||
| 2248 | test "seekBy" { | 2207 | test "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(); |
| 2251 | 2212 | ||
| 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); |
| 2257 | 2218 | ||
| 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 contain | 348 | // 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 | }; |
| 1391 | 1398 | ||
| 1392 | pub const ConnectTcpError = error{ | 1399 | pub 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; |
| 1403 | 1402 | ||
| 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 | }; |
| 1425 | 1424 | ||
| 1426 | pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcpError!*Connection { | 1425 | pub 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; |
| 1439 | 1439 | ||
| 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(); | ||
| 1451 | 1442 | ||
| 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"); |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | const net = std.net; | 6 | const net = std.Io.net; |
| 7 | const posix = std.posix; | 7 | const posix = std.posix; |
| 8 | const linux = std.os.linux; | 8 | const linux = std.os.linux; |
| 9 | const testing = std.testing; | 9 | const 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(); |
| 2363 | 2363 | ||
| 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 | }; | ||
| 2365 | 2368 | ||
| 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)); |
| 2371 | 2374 | ||
| 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); |
| 2375 | 2378 | ||
| 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); |
| 2378 | 2381 | ||
| 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" { |
| 2441 | test "timeout (after a relative time)" { | 2446 | test "timeout (after a relative time)" { |
| 2442 | if (!is_linux) return error.SkipZigTest; | 2447 | if (!is_linux) return error.SkipZigTest; |
| 2443 | 2448 | ||
| 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 }; |
| 2454 | 2461 | ||
| 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); |
| 2461 | 2468 | ||
| 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); |
| 2467 | 2474 | ||
| 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 | } |
| 2471 | 2479 | ||
| 2472 | test "timeout (after a number of completions)" { | 2480 | test "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(); |
| 2863 | 2871 | ||
| 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 | }; | ||
| 2865 | 2876 | ||
| 2866 | // Socket bound, expect shutdown to work | 2877 | // 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); |
| 2873 | 2884 | ||
| 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); |
| 2877 | 2888 | ||
| 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" { |
| 2898 | 2909 | ||
| 2899 | // Socket not bound, expect to fail with ENOTCONN | 2910 | // 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); |
| 2903 | 2914 | ||
| 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); |
| 2967 | 2978 | ||
| 2968 | // Validate that the old file doesn't exist anymore | 2979 | // 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 | } | ||
| 2975 | 2981 | ||
| 2976 | // Validate that the new file exists with the proper content | 2982 | // 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 | } |
| 2986 | 2986 | ||
| 2987 | test "unlinkat" { | 2987 | test "unlinkat" { |
| ... | @@ -3179,12 +3179,8 @@ test "linkat" { | ... | @@ -3179,12 +3179,8 @@ test "linkat" { |
| 3179 | }, cqe); | 3179 | }, cqe); |
| 3180 | 3180 | ||
| 3181 | // Validate the second file | 3181 | // 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 | } |
| 3189 | 3185 | ||
| 3190 | test "provide_buffers: read" { | 3186 | test "provide_buffers: read" { |
| ... | @@ -3588,7 +3584,10 @@ const SocketTestHarness = struct { | ... | @@ -3588,7 +3584,10 @@ const SocketTestHarness = struct { |
| 3588 | 3584 | ||
| 3589 | fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { | 3585 | fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 3590 | // Create a TCP server socket | 3586 | // 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); |
| 3594 | 3593 | ||
| ... | @@ -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); |
| 3599 | 3598 | ||
| 3600 | // Create a TCP client socket | 3599 | // 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)); |
| 3604 | 3603 | ||
| 3605 | try testing.expectEqual(@as(u32, 2), try ring.submit()); | 3604 | try testing.expectEqual(@as(u32, 2), try ring.submit()); |
| 3606 | 3605 | ||
| ... | @@ -3636,18 +3635,18 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { | ... | @@ -3636,18 +3635,18 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 3636 | }; | 3635 | }; |
| 3637 | } | 3636 | } |
| 3638 | 3637 | ||
| 3639 | fn createListenerSocket(address: *net.Address) !posix.socket_t { | 3638 | fn 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); |
| 3643 | 3642 | ||
| 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); |
| 3647 | 3646 | ||
| 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); |
| 3651 | 3650 | ||
| 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(); |
| 3664 | 3663 | ||
| 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); |
| 3668 | 3670 | ||
| ... | @@ -3676,9 +3678,9 @@ test "accept multishot" { | ... | @@ -3676,9 +3678,9 @@ test "accept multishot" { |
| 3676 | var nr: usize = 4; // number of clients to connect | 3678 | var nr: usize = 4; // number of clients to connect |
| 3677 | while (nr > 0) : (nr -= 1) { | 3679 | while (nr > 0) : (nr -= 1) { |
| 3678 | // connect client | 3680 | // 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)); |
| 3682 | 3684 | ||
| 3683 | // test accept completion | 3685 | // 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 | }; | ||
| 3760 | 3765 | ||
| 3761 | // register direct file descriptors | 3766 | // 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()); |
| 3780 | 3785 | ||
| 3781 | // connect | 3786 | // 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); |
| 3785 | 3790 | ||
| 3786 | // accept completion | 3791 | // 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 | // connect | 3820 | // 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 error | 3824 | // 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(); |
| 3839 | 3844 | ||
| 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 | }; | ||
| 3841 | 3849 | ||
| 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" { |
| 3855 | 3863 | ||
| 3856 | for (registered_fds) |_| { | 3864 | for (registered_fds) |_| { |
| 3857 | // connect | 3865 | // 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); |
| 3861 | 3869 | ||
| 3862 | // accept completion | 3870 | // 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 | // connect | 3880 | // 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 error | 3884 | // 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 index | 3952 | try testing.expect(cqe_socket.res == 2); // returns registered file index |
| 3945 | 3953 | ||
| 3946 | // use sockets from registered_fds in connect operation | 3954 | // 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 accept | 3965 | // 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 socket | 3967 | // 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 index | 3969 | connect_sqe.flags |= linux.IOSQE_FIXED_FILE; // fd is fixed file index |
| 3959 | // submit both | 3970 | // 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 operation | 4494 | // LISTEN is higher required operation |
| 4484 | if (!probe.is_supported(.LISTEN)) return error.SkipZigTest; | 4495 | if (!probe.is_supported(.LISTEN)) return error.SkipZigTest; |
| 4485 | 4496 | ||
| 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; | ||
| 4488 | 4502 | ||
| 4489 | const listen_fd = brk: { | 4503 | const listen_fd = brk: { |
| 4490 | // Create socket | 4504 | // 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 operations | 4519 | // 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); |
| 4522 | 4536 | ||
| 4523 | // Read system assigned port into addr | 4537 | // 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); |
| 4526 | 4540 | ||
| 4527 | break :brk listen_fd; | 4541 | break :brk listen_fd; |
| 4528 | }; | 4542 | }; |
| 4529 | 4543 | ||
| 4530 | const connect_fd = brk: { | 4544 | const connect_fd = brk: { |
| 4531 | // Create connect socket | 4545 | // 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" { |
| 4542 | 4556 | ||
| 4543 | // Prepare accept/connect operations | 4557 | // 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 socket | 4561 | // 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 | |||
| 4622 | fn 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 | } |
| 3002 | 3002 | ||
| 3003 | pub const MakeDirError = error{ | 3003 | pub 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; | ||
| 3028 | 3004 | ||
| 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 | } |
| 733 | 733 | ||
| 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 | } |
| 740 | 737 | ||
| 741 | test "writev longer than IOV_MAX" { | 738 | test "writev longer than IOV_MAX" { |
lib/std/process/Child.zig+26-11| ... | @@ -1,5 +1,9 @@ | ... | @@ -1,5 +1,9 @@ |
| 1 | const std = @import("../std.zig"); | 1 | const ChildProcess = @This(); |
| 2 | |||
| 2 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const native_os = builtin.os.tag; | ||
| 5 | |||
| 6 | const std = @import("../std.zig"); | ||
| 3 | const unicode = std.unicode; | 7 | const unicode = std.unicode; |
| 4 | const fs = std.fs; | 8 | const fs = std.fs; |
| 5 | const process = std.process; | 9 | const process = std.process; |
| ... | @@ -11,9 +15,7 @@ const mem = std.mem; | ... | @@ -11,9 +15,7 @@ const mem = std.mem; |
| 11 | const EnvMap = std.process.EnvMap; | 15 | const EnvMap = std.process.EnvMap; |
| 12 | const maxInt = std.math.maxInt; | 16 | const maxInt = std.math.maxInt; |
| 13 | const assert = std.debug.assert; | 17 | const assert = std.debug.assert; |
| 14 | const native_os = builtin.os.tag; | ||
| 15 | const Allocator = std.mem.Allocator; | 18 | const Allocator = std.mem.Allocator; |
| 16 | const ChildProcess = @This(); | ||
| 17 | const ArrayList = std.ArrayList; | 19 | const ArrayList = std.ArrayList; |
| 18 | 20 | ||
| 19 | pub const Id = switch (native_os) { | 21 | pub 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 { |
| 317 | 319 | ||
| 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 | } |
| 332 | 341 | ||
| ... | @@ -1014,8 +1023,14 @@ fn writeIntFd(fd: i32, value: ErrInt) !void { | ... | @@ -1014,8 +1023,14 @@ fn writeIntFd(fd: i32, value: ErrInt) !void { |
| 1014 | 1023 | ||
| 1015 | fn readIntFd(fd: i32) !ErrInt { | 1024 | fn 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 | } |
| 1020 | 1035 | ||
| 1021 | const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); | 1036 | const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); |
lib/std/tar/Writer.zig+20-18| ... | @@ -1,7 +1,9 @@ | ... | @@ -1,7 +1,9 @@ |
| 1 | const Writer = @This(); | ||
| 2 | |||
| 1 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const Io = std.Io; | ||
| 2 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 3 | const testing = std.testing; | 6 | const testing = std.testing; |
| 4 | const Writer = @This(); | ||
| 5 | 7 | ||
| 6 | const block_size = @sizeOf(Header); | 8 | const block_size = @sizeOf(Header); |
| 7 | 9 | ||
| ... | @@ -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 | }; |
| 16 | 18 | ||
| 17 | underlying_writer: *std.Io.Writer, | 19 | underlying_writer: *Io.Writer, |
| 18 | prefix: []const u8 = "", | 20 | prefix: []const u8 = "", |
| 19 | mtime_now: u64 = 0, | 21 | mtime_now: u64 = 0, |
| 20 | 22 | ||
| ... | @@ -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 | } |
| 38 | 40 | ||
| 39 | pub const WriteFileError = std.Io.Writer.FileError || Error || std.fs.File.Reader.SizeError; | 41 | pub const WriteFileError = Io.Writer.FileError || Error || Io.File.Reader.SizeError; |
| 40 | 42 | ||
| 41 | pub fn writeFile( | 43 | pub 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 | } |
| 60 | 62 | ||
| 61 | pub const WriteFileStreamError = Error || std.Io.Reader.StreamError; | 63 | pub const WriteFileStreamError = Error || Io.Reader.StreamError; |
| 62 | 64 | ||
| 63 | /// Writes file reading file content from `reader`. Reads exactly `size` bytes | 65 | /// 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 | } |
| 138 | 140 | ||
| 139 | fn writePadding(w: *Writer, bytes: usize) std.Io.Writer.Error!void { | 141 | fn 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 | } |
| 142 | 144 | ||
| 143 | fn writePadding64(w: *Writer, bytes: u64) std.Io.Writer.Error!void { | 145 | fn 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 | } |
| 146 | 148 | ||
| 147 | fn writePaddingPos(w: *Writer, pos: usize) std.Io.Writer.Error!void { | 149 | fn 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 an | 155 | /// "reasonable system must not assume that such a block exists when reading an |
| 154 | /// archive". Therefore, the Zig standard library recommends to not call this | 156 | /// archive". Therefore, the Zig standard library recommends to not call this |
| 155 | /// function. | 157 | /// function. |
| 156 | pub fn finishPedantically(w: *Writer) std.Io.Writer.Error!void { | 158 | pub 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 | } |
| 159 | 161 | ||
| ... | @@ -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 | } |
| 250 | 252 | ||
| 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"; |
| 398 | 400 | ||
| 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, .{}); |
| 405 | 407 | ||
| 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 ..]); |
| 426 | 428 | ||
| 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 root | 435 | // 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 | } |
| 442 | 444 | ||
| 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); |
| 454 | 456 | ||
| 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 do | 560 | /// will align a byte-sized allocation to at least 2. Allocators that don't do |
| 561 | /// this are rare. | 561 | /// this are rare. |
| 562 | pub fn readSourceFileToEndAlloc(gpa: Allocator, file_reader: *std.fs.File.Reader) ![:0]u8 { | 562 | pub 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); |
| 565 | 565 |
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, | ||
| 445 | 446 | ||
| 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 | } |
| 544 | 545 | ||
| 545 | pub const AbiAndDynamicLinkerFromFileError = error{}; | 546 | fn abiAndDynamicLinkerFromFile( |
| 546 | |||
| 547 | pub 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( |
| 645 | 645 | ||
| 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 same | 660 | // 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, |
| 850 | 849 | ||
| 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 | } |
| 859 | 854 | ||
| ... | @@ -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 }; |
| 885 | 880 | 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 | } |
| 900 | 901 | ||
| ... | @@ -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, |
| 1095 | 1097 | ||
| 1096 | error.ReadFailed => return file_reader.err.?, | 1098 | error.ReadFailed => return file_reader.err.?, |
| 1097 | 1099 | ||
| 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"); |
| 461 | 460 | ||
| 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 | }; |
| 974 | 973 | ||
| 975 | fn 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 | |||
| 983 | fn knownFileExtension(filename: []const u8) bool { | 974 | fn 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{ |