| 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 | const std = @import("std"); | |
| 1 | const runner = @This(); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | ||
| 4 | const std = @import("std"); | |
| 5 | const Io = std.Io; | |
| 3 | 6 | const assert = std.debug.assert; |
| 4 | 7 | const fmt = std.fmt; |
| 5 | 8 | const mem = std.mem; |
| ... | ... | @@ -11,7 +14,6 @@ const WebServer = std.Build.WebServer; |
| 11 | 14 | const Allocator = std.mem.Allocator; |
| 12 | 15 | const fatal = std.process.fatal; |
| 13 | 16 | const Writer = std.Io.Writer; |
| 14 | const runner = @This(); | |
| 15 | 17 | const tty = std.Io.tty; |
| 16 | 18 | |
| 17 | 19 | pub const root = @import("@build"); |
| ... | ... | @@ -75,6 +77,7 @@ pub fn main() !void { |
| 75 | 77 | .io = io, |
| 76 | 78 | .arena = arena, |
| 77 | 79 | .cache = .{ |
| 80 | .io = io, | |
| 78 | 81 | .gpa = arena, |
| 79 | 82 | .manifest_dir = try local_cache_directory.handle.makeOpenPath("h", .{}), |
| 80 | 83 | }, |
| ... | ... | @@ -84,7 +87,7 @@ pub fn main() !void { |
| 84 | 87 | .zig_lib_directory = zig_lib_directory, |
| 85 | 88 | .host = .{ |
| 86 | 89 | .query = .{}, |
| 87 | .result = try std.zig.system.resolveTargetQuery(.{}), | |
| 90 | .result = try std.zig.system.resolveTargetQuery(io, .{}), | |
| 88 | 91 | }, |
| 89 | 92 | .time_report = false, |
| 90 | 93 | }; |
| ... | ... | @@ -121,7 +124,7 @@ pub fn main() !void { |
| 121 | 124 | var watch = false; |
| 122 | 125 | var fuzz: ?std.Build.Fuzz.Mode = null; |
| 123 | 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 | 129 | if (try std.zig.EnvVar.ZIG_BUILD_ERROR_STYLE.get(arena)) |str| { |
| 127 | 130 | if (std.meta.stringToEnum(ErrorStyle, str)) |style| { |
| ... | ... | @@ -288,11 +291,11 @@ pub fn main() !void { |
| 288 | 291 | }); |
| 289 | 292 | }; |
| 290 | 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 | 295 | } else if (mem.startsWith(u8, arg, "--webui=")) { |
| 293 | 296 | const addr_str = arg["--webui=".len..]; |
| 294 | 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 | 299 | fatal("invalid web UI address '{s}': {s}", .{ addr_str, @errorName(err) }); |
| 297 | 300 | }; |
| 298 | 301 | } else if (mem.eql(u8, arg, "--debug-log")) { |
| ... | ... | @@ -334,14 +337,10 @@ pub fn main() !void { |
| 334 | 337 | watch = true; |
| 335 | 338 | } else if (mem.eql(u8, arg, "--time-report")) { |
| 336 | 339 | graph.time_report = true; |
| 337 | if (webui_listen == null) { | |
| 338 | webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable; | |
| 339 | } | |
| 340 | if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) }; | |
| 340 | 341 | } else if (mem.eql(u8, arg, "--fuzz")) { |
| 341 | 342 | fuzz = .{ .forever = undefined }; |
| 342 | if (webui_listen == null) { | |
| 343 | webui_listen = std.net.Address.parseIp("::1", 0) catch unreachable; | |
| 344 | } | |
| 343 | if (webui_listen == null) webui_listen = .{ .ip6 = .loopback(0) }; | |
| 345 | 344 | } else if (mem.startsWith(u8, arg, "--fuzz=")) { |
| 346 | 345 | const value = arg["--fuzz=".len..]; |
| 347 | 346 | if (value.len == 0) fatal("missing argument to --fuzz", .{}); |
| ... | ... | @@ -550,13 +549,15 @@ pub fn main() !void { |
| 550 | 549 | |
| 551 | 550 | var w: Watch = w: { |
| 552 | 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 | 553 | break :w try .init(); |
| 555 | 554 | }; |
| 556 | 555 | |
| 557 | 556 | try run.thread_pool.init(thread_pool_options); |
| 558 | 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 | 561 | run.web_server = if (webui_listen) |listen_address| ws: { |
| 561 | 562 | if (builtin.single_threaded) unreachable; // `fatal` above |
| 562 | 563 | break :ws .init(.{ |
| ... | ... | @@ -568,11 +569,12 @@ pub fn main() !void { |
| 568 | 569 | .root_prog_node = main_progress_node, |
| 569 | 570 | .watch = watch, |
| 570 | 571 | .listen_address = listen_address, |
| 572 | .base_timestamp = now, | |
| 571 | 573 | }); |
| 572 | 574 | } else null; |
| 573 | 575 | |
| 574 | 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 | 580 | rebuild: while (true) : (if (run.error_style.clearOnUpdate()) { |
| ... | ... | @@ -755,6 +757,7 @@ fn runStepNames( |
| 755 | 757 | fuzz: ?std.Build.Fuzz.Mode, |
| 756 | 758 | ) !void { |
| 757 | 759 | const gpa = run.gpa; |
| 760 | const io = b.graph.io; | |
| 758 | 761 | const step_stack = &run.step_stack; |
| 759 | 762 | const thread_pool = &run.thread_pool; |
| 760 | 763 | |
| ... | ... | @@ -858,6 +861,7 @@ fn runStepNames( |
| 858 | 861 | assert(mode == .limit); |
| 859 | 862 | var f = std.Build.Fuzz.init( |
| 860 | 863 | gpa, |
| 864 | io, | |
| 861 | 865 | thread_pool, |
| 862 | 866 | step_stack.keys(), |
| 863 | 867 | parent_prog_node, |
lib/compiler/test_runner.zig+7-8| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | const std = @import("std"); |
| 5 | const Io = std.Io; | |
| 5 | 6 | const fatal = std.process.fatal; |
| 6 | 7 | const testing = std.testing; |
| 7 | 8 | const assert = std.debug.assert; |
| ... | ... | @@ -16,6 +17,7 @@ var fba: std.heap.FixedBufferAllocator = .init(&fba_buffer); |
| 16 | 17 | var fba_buffer: [8192]u8 = undefined; |
| 17 | 18 | var stdin_buffer: [4096]u8 = undefined; |
| 18 | 19 | var stdout_buffer: [4096]u8 = undefined; |
| 20 | var runner_threaded_io: Io.Threaded = .init_single_threaded; | |
| 19 | 21 | |
| 20 | 22 | /// Keep in sync with logic in `std.Build.addRunArtifact` which decides whether |
| 21 | 23 | /// the test runner will communicate with the build runner via `std.zig.Server`. |
| ... | ... | @@ -63,8 +65,6 @@ pub fn main() void { |
| 63 | 65 | fuzz_abi.fuzzer_init(.fromSlice(cache_dir)); |
| 64 | 66 | } |
| 65 | 67 | |
| 66 | fba.reset(); | |
| 67 | ||
| 68 | 68 | if (listen) { |
| 69 | 69 | return mainServer() catch @panic("internal test runner failure"); |
| 70 | 70 | } else { |
| ... | ... | @@ -74,7 +74,7 @@ pub fn main() void { |
| 74 | 74 | |
| 75 | 75 | fn mainServer() !void { |
| 76 | 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 | 78 | var stdout_writer = std.fs.File.stdout().writerStreaming(&stdout_buffer); |
| 79 | 79 | var server = try std.zig.Server.init(.{ |
| 80 | 80 | .in = &stdin_reader.interface, |
| ... | ... | @@ -131,7 +131,7 @@ fn mainServer() !void { |
| 131 | 131 | |
| 132 | 132 | .run_test => { |
| 133 | 133 | testing.allocator_instance = .{}; |
| 134 | testing.io_instance = .init(fba.allocator()); | |
| 134 | testing.io_instance = .init(testing.allocator); | |
| 135 | 135 | log_err_count = 0; |
| 136 | 136 | const index = try server.receiveBody_u32(); |
| 137 | 137 | const test_fn = builtin.test_functions[index]; |
| ... | ... | @@ -154,7 +154,6 @@ fn mainServer() !void { |
| 154 | 154 | }, |
| 155 | 155 | }; |
| 156 | 156 | testing.io_instance.deinit(); |
| 157 | fba.reset(); | |
| 158 | 157 | const leak_count = testing.allocator_instance.detectLeaks(); |
| 159 | 158 | testing.allocator_instance.deinitWithoutLeakChecks(); |
| 160 | 159 | try server.serveTestResults(.{ |
| ... | ... | @@ -234,10 +233,10 @@ fn mainTerminal() void { |
| 234 | 233 | var leaks: usize = 0; |
| 235 | 234 | for (test_fn_list, 0..) |test_fn, i| { |
| 236 | 235 | testing.allocator_instance = .{}; |
| 237 | testing.io_instance = .init(fba.allocator()); | |
| 236 | testing.io_instance = .init(testing.allocator); | |
| 238 | 237 | defer { |
| 239 | if (testing.allocator_instance.deinit() == .leak) leaks += 1; | |
| 240 | 238 | testing.io_instance.deinit(); |
| 239 | if (testing.allocator_instance.deinit() == .leak) leaks += 1; | |
| 241 | 240 | } |
| 242 | 241 | testing.log_level = .warn; |
| 243 | 242 | |
| ... | ... | @@ -324,7 +323,7 @@ pub fn mainSimple() anyerror!void { |
| 324 | 323 | .stage2_aarch64, .stage2_riscv64 => true, |
| 325 | 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 | 327 | const enable_print = switch (builtin.zig_backend) { |
| 329 | 328 | .stage2_aarch64, .stage2_riscv64 => true, |
| 330 | 329 | else => false, |
lib/std/Build.zig+3-1| ... | ... | @@ -1837,6 +1837,8 @@ pub fn runAllowFail( |
| 1837 | 1837 | if (!process.can_spawn) |
| 1838 | 1838 | return error.ExecNotSupported; |
| 1839 | 1839 | |
| 1840 | const io = b.graph.io; | |
| 1841 | ||
| 1840 | 1842 | const max_output_size = 400 * 1024; |
| 1841 | 1843 | var child = std.process.Child.init(argv, b.allocator); |
| 1842 | 1844 | child.stdin_behavior = .Ignore; |
| ... | ... | @@ -1847,7 +1849,7 @@ pub fn runAllowFail( |
| 1847 | 1849 | try Step.handleVerbose2(b, null, child.env_map, argv); |
| 1848 | 1850 | try child.spawn(); |
| 1849 | 1851 | |
| 1850 | var stdout_reader = child.stdout.?.readerStreaming(&.{}); | |
| 1852 | var stdout_reader = child.stdout.?.readerStreaming(io, &.{}); | |
| 1851 | 1853 | const stdout = stdout_reader.interface.allocRemaining(b.allocator, .limited(max_output_size)) catch { |
| 1852 | 1854 | return error.ReadFailure; |
| 1853 | 1855 | }; |
lib/std/Build/Cache.zig+16-6| ... | ... | @@ -3,8 +3,10 @@ |
| 3 | 3 | //! not to withstand attacks using specially-crafted input. |
| 4 | 4 | |
| 5 | 5 | const Cache = @This(); |
| 6 | const std = @import("std"); | |
| 7 | 6 | const builtin = @import("builtin"); |
| 7 | ||
| 8 | const std = @import("std"); | |
| 9 | const Io = std.Io; | |
| 8 | 10 | const crypto = std.crypto; |
| 9 | 11 | const fs = std.fs; |
| 10 | 12 | const assert = std.debug.assert; |
| ... | ... | @@ -15,6 +17,7 @@ const Allocator = std.mem.Allocator; |
| 15 | 17 | const log = std.log.scoped(.cache); |
| 16 | 18 | |
| 17 | 19 | gpa: Allocator, |
| 20 | io: Io, | |
| 18 | 21 | manifest_dir: fs.Dir, |
| 19 | 22 | hash: HashHelper = .{}, |
| 20 | 23 | /// This value is accessed from multiple threads, protected by mutex. |
| ... | ... | @@ -661,9 +664,10 @@ pub const Manifest = struct { |
| 661 | 664 | }, |
| 662 | 665 | } { |
| 663 | 666 | const gpa = self.cache.gpa; |
| 667 | const io = self.cache.io; | |
| 664 | 668 | const input_file_count = self.files.entries.len; |
| 665 | 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 | 671 | const limit: std.Io.Limit = .limited(manifest_file_size_max); |
| 668 | 672 | const file_contents = manifest_reader.interface.allocRemaining(gpa, limit) catch |err| switch (err) { |
| 669 | 673 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -1337,7 +1341,8 @@ test "cache file and then recall it" { |
| 1337 | 1341 | var digest2: HexDigest = undefined; |
| 1338 | 1342 | |
| 1339 | 1343 | { |
| 1340 | var cache = Cache{ | |
| 1344 | var cache: Cache = .{ | |
| 1345 | .io = io, | |
| 1341 | 1346 | .gpa = testing.allocator, |
| 1342 | 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 | 1407 | var digest2: HexDigest = undefined; |
| 1403 | 1408 | |
| 1404 | 1409 | { |
| 1405 | var cache = Cache{ | |
| 1410 | var cache: Cache = .{ | |
| 1411 | .io = io, | |
| 1406 | 1412 | .gpa = testing.allocator, |
| 1407 | 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 | 1457 | } |
| 1452 | 1458 | |
| 1453 | 1459 | test "no file inputs" { |
| 1460 | const io = testing.io; | |
| 1461 | ||
| 1454 | 1462 | var tmp = testing.tmpDir(.{}); |
| 1455 | 1463 | defer tmp.cleanup(); |
| 1456 | 1464 | |
| ... | ... | @@ -1459,7 +1467,8 @@ test "no file inputs" { |
| 1459 | 1467 | var digest1: HexDigest = undefined; |
| 1460 | 1468 | var digest2: HexDigest = undefined; |
| 1461 | 1469 | |
| 1462 | var cache = Cache{ | |
| 1470 | var cache: Cache = .{ | |
| 1471 | .io = io, | |
| 1463 | 1472 | .gpa = testing.allocator, |
| 1464 | 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 | 1526 | var digest3: HexDigest = undefined; |
| 1518 | 1527 | |
| 1519 | 1528 | { |
| 1520 | var cache = Cache{ | |
| 1529 | var cache: Cache = .{ | |
| 1530 | .io = io, | |
| 1521 | 1531 | .gpa = testing.allocator, |
| 1522 | 1532 | .manifest_dir = try tmp.dir.makeOpenPath(temp_manifest_dir, .{}), |
| 1523 | 1533 | }; |
lib/std/Build/Fuzz.zig+6-1| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | const Io = std.Io; | |
| 2 | 3 | const Build = std.Build; |
| 3 | 4 | const Cache = Build.Cache; |
| 4 | 5 | const Step = std.Build.Step; |
| ... | ... | @@ -14,6 +15,7 @@ const Fuzz = @This(); |
| 14 | 15 | const build_runner = @import("root"); |
| 15 | 16 | |
| 16 | 17 | gpa: Allocator, |
| 18 | io: Io, | |
| 17 | 19 | mode: Mode, |
| 18 | 20 | |
| 19 | 21 | /// Allocated into `gpa`. |
| ... | ... | @@ -75,6 +77,7 @@ const CoverageMap = struct { |
| 75 | 77 | |
| 76 | 78 | pub fn init( |
| 77 | 79 | gpa: Allocator, |
| 80 | io: Io, | |
| 78 | 81 | thread_pool: *std.Thread.Pool, |
| 79 | 82 | all_steps: []const *Build.Step, |
| 80 | 83 | root_prog_node: std.Progress.Node, |
| ... | ... | @@ -111,6 +114,7 @@ pub fn init( |
| 111 | 114 | |
| 112 | 115 | return .{ |
| 113 | 116 | .gpa = gpa, |
| 117 | .io = io, | |
| 114 | 118 | .mode = mode, |
| 115 | 119 | .run_steps = run_steps, |
| 116 | 120 | .wait_group = .{}, |
| ... | ... | @@ -484,6 +488,7 @@ fn addEntryPoint(fuzz: *Fuzz, coverage_id: u64, addr: u64) error{ AlreadyReporte |
| 484 | 488 | |
| 485 | 489 | pub fn waitAndPrintReport(fuzz: *Fuzz) void { |
| 486 | 490 | assert(fuzz.mode == .limit); |
| 491 | const io = fuzz.io; | |
| 487 | 492 | |
| 488 | 493 | fuzz.wait_group.wait(); |
| 489 | 494 | fuzz.wait_group.reset(); |
| ... | ... | @@ -506,7 +511,7 @@ pub fn waitAndPrintReport(fuzz: *Fuzz) void { |
| 506 | 511 | |
| 507 | 512 | const fuzz_abi = std.Build.abi.fuzz; |
| 508 | 513 | var rbuf: [0x1000]u8 = undefined; |
| 509 | var r = coverage_file.reader(&rbuf); | |
| 514 | var r = coverage_file.reader(io, &rbuf); | |
| 510 | 515 | |
| 511 | 516 | var header: fuzz_abi.SeenPcsHeader = undefined; |
| 512 | 517 | r.interface.readSliceAll(std.mem.asBytes(&header)) catch |err| { |
lib/std/Build/Step.zig+11-8| ... | ... | @@ -1,9 +1,11 @@ |
| 1 | 1 | const Step = @This(); |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 2 | 4 | const std = @import("../std.zig"); |
| 5 | const Io = std.Io; | |
| 3 | 6 | const Build = std.Build; |
| 4 | 7 | const Allocator = std.mem.Allocator; |
| 5 | 8 | const assert = std.debug.assert; |
| 6 | const builtin = @import("builtin"); | |
| 7 | 9 | const Cache = Build.Cache; |
| 8 | 10 | const Path = Cache.Path; |
| 9 | 11 | const ArrayList = std.ArrayList; |
| ... | ... | @@ -327,7 +329,7 @@ pub fn cast(step: *Step, comptime T: type) ?*T { |
| 327 | 329 | } |
| 328 | 330 | |
| 329 | 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 | 333 | if (step.debug_stack_trace.instruction_addresses.len > 0) { |
| 332 | 334 | w.print("name: '{s}'. creation stack trace:\n", .{step.name}) catch {}; |
| 333 | 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 | 384 | |
| 383 | 385 | pub const ZigProcess = struct { |
| 384 | 386 | child: std.process.Child, |
| 385 | poller: std.Io.Poller(StreamEnum), | |
| 387 | poller: Io.Poller(StreamEnum), | |
| 386 | 388 | progress_ipc_fd: if (std.Progress.have_ipc) ?std.posix.fd_t else void, |
| 387 | 389 | |
| 388 | 390 | pub const StreamEnum = enum { stdout, stderr }; |
| ... | ... | @@ -458,7 +460,7 @@ pub fn evalZigProcess( |
| 458 | 460 | const zp = try gpa.create(ZigProcess); |
| 459 | 461 | zp.* = .{ |
| 460 | 462 | .child = child, |
| 461 | .poller = std.Io.poll(gpa, ZigProcess.StreamEnum, .{ | |
| 463 | .poller = Io.poll(gpa, ZigProcess.StreamEnum, .{ | |
| 462 | 464 | .stdout = child.stdout.?, |
| 463 | 465 | .stderr = child.stderr.?, |
| 464 | 466 | }), |
| ... | ... | @@ -505,11 +507,12 @@ pub fn evalZigProcess( |
| 505 | 507 | } |
| 506 | 508 | |
| 507 | 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 | 511 | const b = s.owner; |
| 512 | const io = b.graph.io; | |
| 510 | 513 | const src_path = src_lazy_path.getPath3(b, s); |
| 511 | 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 | 516 | return s.fail("unable to update file from '{f}' to '{s}': {s}", .{ |
| 514 | 517 | src_path, dest_path, @errorName(err), |
| 515 | 518 | }); |
| ... | ... | @@ -738,7 +741,7 @@ pub fn allocPrintCmd2( |
| 738 | 741 | argv: []const []const u8, |
| 739 | 742 | ) Allocator.Error![]u8 { |
| 740 | 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 | 745 | for (string) |c| { |
| 743 | 746 | if (switch (c) { |
| 744 | 747 | else => true, |
| ... | ... | @@ -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 | 779 | defer aw.deinit(); |
| 777 | 780 | const writer = &aw.writer; |
| 778 | 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 | 538 | defer arena.deinit(); |
| 539 | 539 | |
| 540 | 540 | var graph: std.Build.Graph = .{ |
| 541 | .io = io, | |
| 541 | 542 | .arena = arena.allocator(), |
| 542 | 543 | .cache = .{ |
| 544 | .io = io, | |
| 543 | 545 | .gpa = arena.allocator(), |
| 544 | 546 | .manifest_dir = std.fs.cwd(), |
| 545 | 547 | }, |
lib/std/Build/Step/Run.zig+8-5| ... | ... | @@ -761,6 +761,7 @@ const IndexedOutput = struct { |
| 761 | 761 | }; |
| 762 | 762 | fn make(step: *Step, options: Step.MakeOptions) !void { |
| 763 | 763 | const b = step.owner; |
| 764 | const io = b.graph.io; | |
| 764 | 765 | const arena = b.allocator; |
| 765 | 766 | const run: *Run = @fieldParentPtr("step", step); |
| 766 | 767 | const has_side_effects = run.hasSideEffects(); |
| ... | ... | @@ -834,7 +835,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 834 | 835 | defer file.close(); |
| 835 | 836 | |
| 836 | 837 | var buf: [1024]u8 = undefined; |
| 837 | var file_reader = file.reader(&buf); | |
| 838 | var file_reader = file.reader(io, &buf); | |
| 838 | 839 | _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) { |
| 839 | 840 | error.ReadFailed => return step.fail( |
| 840 | 841 | "failed to read from '{f}': {t}", |
| ... | ... | @@ -1067,6 +1068,7 @@ pub fn rerunInFuzzMode( |
| 1067 | 1068 | ) !void { |
| 1068 | 1069 | const step = &run.step; |
| 1069 | 1070 | const b = step.owner; |
| 1071 | const io = b.graph.io; | |
| 1070 | 1072 | const arena = b.allocator; |
| 1071 | 1073 | var argv_list: std.ArrayList([]const u8) = .empty; |
| 1072 | 1074 | for (run.argv.items) |arg| { |
| ... | ... | @@ -1093,7 +1095,7 @@ pub fn rerunInFuzzMode( |
| 1093 | 1095 | defer file.close(); |
| 1094 | 1096 | |
| 1095 | 1097 | var buf: [1024]u8 = undefined; |
| 1096 | var file_reader = file.reader(&buf); | |
| 1098 | var file_reader = file.reader(io, &buf); | |
| 1097 | 1099 | _ = file_reader.interface.streamRemaining(&result.writer) catch |err| switch (err) { |
| 1098 | 1100 | error.ReadFailed => return file_reader.err.?, |
| 1099 | 1101 | error.WriteFailed => return error.OutOfMemory, |
| ... | ... | @@ -2090,6 +2092,7 @@ fn sendRunFuzzTestMessage( |
| 2090 | 2092 | |
| 2091 | 2093 | fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { |
| 2092 | 2094 | const b = run.step.owner; |
| 2095 | const io = b.graph.io; | |
| 2093 | 2096 | const arena = b.allocator; |
| 2094 | 2097 | |
| 2095 | 2098 | try child.spawn(); |
| ... | ... | @@ -2113,7 +2116,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { |
| 2113 | 2116 | defer file.close(); |
| 2114 | 2117 | // TODO https://github.com/ziglang/zig/issues/23955 |
| 2115 | 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 | 2120 | var write_buffer: [1024]u8 = undefined; |
| 2118 | 2121 | var stdin_writer = child.stdin.?.writer(&write_buffer); |
| 2119 | 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 | 2162 | stdout_bytes = try poller.toOwnedSlice(.stdout); |
| 2160 | 2163 | stderr_bytes = try poller.toOwnedSlice(.stderr); |
| 2161 | 2164 | } else { |
| 2162 | var stdout_reader = stdout.readerStreaming(&.{}); | |
| 2165 | var stdout_reader = stdout.readerStreaming(io, &.{}); | |
| 2163 | 2166 | stdout_bytes = stdout_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { |
| 2164 | 2167 | error.OutOfMemory => return error.OutOfMemory, |
| 2165 | 2168 | error.ReadFailed => return stdout_reader.err.?, |
| ... | ... | @@ -2167,7 +2170,7 @@ fn evalGeneric(run: *Run, child: *std.process.Child) !EvalGenericResult { |
| 2167 | 2170 | }; |
| 2168 | 2171 | } |
| 2169 | 2172 | } else if (child.stderr) |stderr| { |
| 2170 | var stderr_reader = stderr.readerStreaming(&.{}); | |
| 2173 | var stderr_reader = stderr.readerStreaming(io, &.{}); | |
| 2171 | 2174 | stderr_bytes = stderr_reader.interface.allocRemaining(arena, run.stdio_limit) catch |err| switch (err) { |
| 2172 | 2175 | error.OutOfMemory => return error.OutOfMemory, |
| 2173 | 2176 | error.ReadFailed => return stderr_reader.err.?, |
lib/std/Build/Step/UpdateSourceFiles.zig+13-11| ... | ... | @@ -3,11 +3,13 @@ |
| 3 | 3 | //! not be used during the normal build process, but as a utility run by a |
| 4 | 4 | //! developer with intention to update source files, which will then be |
| 5 | 5 | //! committed to version control. |
| 6 | const UpdateSourceFiles = @This(); | |
| 7 | ||
| 6 | 8 | const std = @import("std"); |
| 9 | const Io = std.Io; | |
| 7 | 10 | const Step = std.Build.Step; |
| 8 | 11 | const fs = std.fs; |
| 9 | 12 | const ArrayList = std.ArrayList; |
| 10 | const UpdateSourceFiles = @This(); | |
| 11 | 13 | |
| 12 | 14 | step: Step, |
| 13 | 15 | output_source_files: std.ArrayListUnmanaged(OutputSourceFile), |
| ... | ... | @@ -70,22 +72,21 @@ pub fn addBytesToSource(usf: *UpdateSourceFiles, bytes: []const u8, sub_path: [] |
| 70 | 72 | fn make(step: *Step, options: Step.MakeOptions) !void { |
| 71 | 73 | _ = options; |
| 72 | 74 | const b = step.owner; |
| 75 | const io = b.graph.io; | |
| 73 | 76 | const usf: *UpdateSourceFiles = @fieldParentPtr("step", step); |
| 74 | 77 | |
| 75 | 78 | var any_miss = false; |
| 76 | 79 | for (usf.output_source_files.items) |output_source_file| { |
| 77 | 80 | if (fs.path.dirname(output_source_file.sub_path)) |dirname| { |
| 78 | 81 | b.build_root.handle.makePath(dirname) catch |err| { |
| 79 | return step.fail("unable to make path '{f}{s}': {s}", .{ | |
| 80 | b.build_root, dirname, @errorName(err), | |
| 81 | }); | |
| 82 | return step.fail("unable to make path '{f}{s}': {t}", .{ b.build_root, dirname, err }); | |
| 82 | 83 | }; |
| 83 | 84 | } |
| 84 | 85 | switch (output_source_file.contents) { |
| 85 | 86 | .bytes => |bytes| { |
| 86 | 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 | b.build_root, output_source_file.sub_path, @errorName(err), | |
| 88 | return step.fail("unable to write file '{f}{s}': {t}", .{ | |
| 89 | b.build_root, output_source_file.sub_path, err, | |
| 89 | 90 | }); |
| 90 | 91 | }; |
| 91 | 92 | any_miss = true; |
| ... | ... | @@ -94,15 +95,16 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 94 | 95 | if (!step.inputs.populated()) try step.addWatchInput(file_source); |
| 95 | 96 | |
| 96 | 97 | const source_path = file_source.getPath2(b, step); |
| 97 | const prev_status = fs.Dir.updateFile( | |
| 98 | fs.cwd(), | |
| 98 | const prev_status = Io.Dir.updateFile( | |
| 99 | .cwd(), | |
| 100 | io, | |
| 99 | 101 | source_path, |
| 100 | b.build_root.handle, | |
| 102 | b.build_root.handle.adaptToNewApi(), | |
| 101 | 103 | output_source_file.sub_path, |
| 102 | 104 | .{}, |
| 103 | 105 | ) catch |err| { |
| 104 | return step.fail("unable to update file from '{s}' to '{f}{s}': {s}", .{ | |
| 105 | source_path, b.build_root, output_source_file.sub_path, @errorName(err), | |
| 106 | return step.fail("unable to update file from '{s}' to '{f}{s}': {t}", .{ | |
| 107 | source_path, b.build_root, output_source_file.sub_path, err, | |
| 106 | 108 | }); |
| 107 | 109 | }; |
| 108 | 110 | any_miss = any_miss or prev_status == .stale; |
lib/std/Build/Step/WriteFile.zig+13-23| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | //! the local cache which has a set of files that have either been generated |
| 3 | 3 | //! during the build, or are copied from the source package. |
| 4 | 4 | const std = @import("std"); |
| 5 | const Io = std.Io; | |
| 5 | 6 | const Step = std.Build.Step; |
| 6 | 7 | const fs = std.fs; |
| 7 | 8 | const ArrayList = std.ArrayList; |
| ... | ... | @@ -174,6 +175,7 @@ fn maybeUpdateName(write_file: *WriteFile) void { |
| 174 | 175 | fn make(step: *Step, options: Step.MakeOptions) !void { |
| 175 | 176 | _ = options; |
| 176 | 177 | const b = step.owner; |
| 178 | const io = b.graph.io; | |
| 177 | 179 | const arena = b.allocator; |
| 178 | 180 | const gpa = arena; |
| 179 | 181 | const write_file: *WriteFile = @fieldParentPtr("step", step); |
| ... | ... | @@ -264,40 +266,27 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 264 | 266 | }; |
| 265 | 267 | defer cache_dir.close(); |
| 266 | 268 | |
| 267 | const cwd = fs.cwd(); | |
| 268 | ||
| 269 | 269 | for (write_file.files.items) |file| { |
| 270 | 270 | if (fs.path.dirname(file.sub_path)) |dirname| { |
| 271 | 271 | cache_dir.makePath(dirname) catch |err| { |
| 272 | return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{ | |
| 273 | b.cache_root, cache_path, fs.path.sep, dirname, @errorName(err), | |
| 272 | return step.fail("unable to make path '{f}{s}{c}{s}': {t}", .{ | |
| 273 | b.cache_root, cache_path, fs.path.sep, dirname, err, | |
| 274 | 274 | }); |
| 275 | 275 | }; |
| 276 | 276 | } |
| 277 | 277 | switch (file.contents) { |
| 278 | 278 | .bytes => |bytes| { |
| 279 | 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}", .{ | |
| 281 | b.cache_root, cache_path, fs.path.sep, file.sub_path, @errorName(err), | |
| 280 | return step.fail("unable to write file '{f}{s}{c}{s}': {t}", .{ | |
| 281 | b.cache_root, cache_path, fs.path.sep, file.sub_path, err, | |
| 282 | 282 | }); |
| 283 | 283 | }; |
| 284 | 284 | }, |
| 285 | 285 | .copy => |file_source| { |
| 286 | 286 | const source_path = file_source.getPath2(b, step); |
| 287 | const prev_status = fs.Dir.updateFile( | |
| 288 | cwd, | |
| 289 | source_path, | |
| 290 | cache_dir, | |
| 291 | file.sub_path, | |
| 292 | .{}, | |
| 293 | ) catch |err| { | |
| 294 | return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {s}", .{ | |
| 295 | source_path, | |
| 296 | b.cache_root, | |
| 297 | cache_path, | |
| 298 | fs.path.sep, | |
| 299 | file.sub_path, | |
| 300 | @errorName(err), | |
| 287 | const prev_status = Io.Dir.updateFile(.cwd(), io, source_path, cache_dir.adaptToNewApi(), file.sub_path, .{}) catch |err| { | |
| 288 | return step.fail("unable to update file from '{s}' to '{f}{s}{c}{s}': {t}", .{ | |
| 289 | source_path, b.cache_root, cache_path, fs.path.sep, file.sub_path, err, | |
| 301 | 290 | }); |
| 302 | 291 | }; |
| 303 | 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 | 320 | switch (entry.kind) { |
| 332 | 321 | .directory => try cache_dir.makePath(dest_path), |
| 333 | 322 | .file => { |
| 334 | const prev_status = fs.Dir.updateFile( | |
| 335 | src_entry_path.root_dir.handle, | |
| 323 | const prev_status = Io.Dir.updateFile( | |
| 324 | src_entry_path.root_dir.handle.adaptToNewApi(), | |
| 325 | io, | |
| 336 | 326 | src_entry_path.sub_path, |
| 337 | cache_dir, | |
| 327 | cache_dir.adaptToNewApi(), | |
| 338 | 328 | dest_path, |
| 339 | 329 | .{}, |
| 340 | 330 | ) catch |err| { |
lib/std/Build/WebServer.zig+40-27| ... | ... | @@ -3,14 +3,15 @@ thread_pool: *std.Thread.Pool, |
| 3 | 3 | graph: *const Build.Graph, |
| 4 | 4 | all_steps: []const *Build.Step, |
| 5 | 5 | listen_address: net.IpAddress, |
| 6 | ttyconf: std.Io.tty.Config, | |
| 6 | ttyconf: Io.tty.Config, | |
| 7 | 7 | root_prog_node: std.Progress.Node, |
| 8 | 8 | watch: bool, |
| 9 | 9 | |
| 10 | 10 | tcp_server: ?net.Server, |
| 11 | 11 | serve_thread: ?std.Thread, |
| 12 | 12 | |
| 13 | base_timestamp: i128, | |
| 13 | /// Uses `Io.Clock.awake`. | |
| 14 | base_timestamp: i96, | |
| 14 | 15 | /// The "step name" data which trails `abi.Hello`, for the steps in `all_steps`. |
| 15 | 16 | step_names_trailing: []u8, |
| 16 | 17 | |
| ... | ... | @@ -53,15 +54,17 @@ pub const Options = struct { |
| 53 | 54 | thread_pool: *std.Thread.Pool, |
| 54 | 55 | graph: *const std.Build.Graph, |
| 55 | 56 | all_steps: []const *Build.Step, |
| 56 | ttyconf: std.Io.tty.Config, | |
| 57 | ttyconf: Io.tty.Config, | |
| 57 | 58 | root_prog_node: std.Progress.Node, |
| 58 | 59 | watch: bool, |
| 59 | 60 | listen_address: net.IpAddress, |
| 61 | base_timestamp: Io.Timestamp, | |
| 60 | 62 | }; |
| 61 | 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 | 65 | // instead of threads, so that the web server can function in single-threaded builds. |
| 64 | 66 | comptime assert(!builtin.single_threaded); |
| 67 | assert(opts.base_timestamp.clock == .awake); | |
| 65 | 68 | |
| 66 | 69 | const all_steps = opts.all_steps; |
| 67 | 70 | |
| ... | ... | @@ -106,7 +109,7 @@ pub fn init(opts: Options) WebServer { |
| 106 | 109 | .tcp_server = null, |
| 107 | 110 | .serve_thread = null, |
| 108 | 111 | |
| 109 | .base_timestamp = std.time.nanoTimestamp(), | |
| 112 | .base_timestamp = opts.base_timestamp.nanoseconds, | |
| 110 | 113 | .step_names_trailing = step_names_trailing, |
| 111 | 114 | |
| 112 | 115 | .step_status_bits = step_status_bits, |
| ... | ... | @@ -147,32 +150,34 @@ pub fn deinit(ws: *WebServer) void { |
| 147 | 150 | pub fn start(ws: *WebServer) error{AlreadyReported}!void { |
| 148 | 151 | assert(ws.tcp_server == null); |
| 149 | 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 | 156 | log.err("failed to listen to port {d}: {s}", .{ ws.listen_address.getPort(), @errorName(err) }); |
| 153 | 157 | return error.AlreadyReported; |
| 154 | 158 | }; |
| 155 | 159 | ws.serve_thread = std.Thread.spawn(.{}, serve, .{ws}) catch |err| { |
| 156 | 160 | log.err("unable to spawn web server thread: {s}", .{@errorName(err)}); |
| 157 | ws.tcp_server.?.deinit(); | |
| 161 | ws.tcp_server.?.deinit(io); | |
| 158 | 162 | ws.tcp_server = null; |
| 159 | 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 | 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 | 171 | fn serve(ws: *WebServer) void { |
| 172 | const io = ws.graph.io; | |
| 168 | 173 | while (true) { |
| 169 | const connection = ws.tcp_server.?.accept() catch |err| { | |
| 174 | var stream = ws.tcp_server.?.accept(io) catch |err| { | |
| 170 | 175 | log.err("failed to accept connection: {s}", .{@errorName(err)}); |
| 171 | 176 | return; |
| 172 | 177 | }; |
| 173 | _ = std.Thread.spawn(.{}, accept, .{ ws, connection }) catch |err| { | |
| 178 | _ = std.Thread.spawn(.{}, accept, .{ ws, stream }) catch |err| { | |
| 174 | 179 | log.err("unable to spawn connection thread: {s}", .{@errorName(err)}); |
| 175 | connection.stream.close(); | |
| 180 | stream.close(io); | |
| 176 | 181 | continue; |
| 177 | 182 | }; |
| 178 | 183 | } |
| ... | ... | @@ -227,6 +232,7 @@ pub fn finishBuild(ws: *WebServer, opts: struct { |
| 227 | 232 | |
| 228 | 233 | ws.fuzz = Fuzz.init( |
| 229 | 234 | ws.gpa, |
| 235 | ws.graph.io, | |
| 230 | 236 | ws.thread_pool, |
| 231 | 237 | ws.all_steps, |
| 232 | 238 | ws.root_prog_node, |
| ... | ... | @@ -241,17 +247,25 @@ pub fn finishBuild(ws: *WebServer, opts: struct { |
| 241 | 247 | } |
| 242 | 248 | |
| 243 | 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 { | |
| 248 | defer connection.stream.close(); | |
| 249 | ||
| 256 | fn accept(ws: *WebServer, stream: net.Stream) void { | |
| 257 | const io = ws.graph.io; | |
| 258 | defer { | |
| 259 | // `net.Stream.close` wants to helpfully overwrite `stream` with | |
| 260 | // `undefined`, but it cannot do so since it is an immutable parameter. | |
| 261 | var copy = stream; | |
| 262 | copy.close(io); | |
| 263 | } | |
| 250 | 264 | var send_buffer: [4096]u8 = undefined; |
| 251 | 265 | var recv_buffer: [4096]u8 = undefined; |
| 252 | var connection_reader = connection.stream.reader(&recv_buffer); | |
| 253 | var connection_writer = connection.stream.writer(&send_buffer); | |
| 254 | var server: http.Server = .init(connection_reader.interface(), &connection_writer.interface); | |
| 266 | var connection_reader = stream.reader(io, &recv_buffer); | |
| 267 | var connection_writer = stream.writer(io, &send_buffer); | |
| 268 | var server: http.Server = .init(&connection_reader.interface, &connection_writer.interface); | |
| 255 | 269 | |
| 256 | 270 | while (true) { |
| 257 | 271 | var request = server.receiveHead() catch |err| switch (err) { |
| ... | ... | @@ -466,12 +480,9 @@ pub fn serveFile( |
| 466 | 480 | }, |
| 467 | 481 | }); |
| 468 | 482 | } |
| 469 | pub fn serveTarFile( | |
| 470 | ws: *WebServer, | |
| 471 | request: *http.Server.Request, | |
| 472 | paths: []const Cache.Path, | |
| 473 | ) !void { | |
| 483 | pub fn serveTarFile(ws: *WebServer, request: *http.Server.Request, paths: []const Cache.Path) !void { | |
| 474 | 484 | const gpa = ws.gpa; |
| 485 | const io = ws.graph.io; | |
| 475 | 486 | |
| 476 | 487 | var send_buffer: [0x4000]u8 = undefined; |
| 477 | 488 | var response = try request.respondStreaming(&send_buffer, .{ |
| ... | ... | @@ -496,7 +507,7 @@ pub fn serveTarFile( |
| 496 | 507 | defer file.close(); |
| 497 | 508 | const stat = try file.stat(); |
| 498 | 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 | 512 | // TODO: this logic is completely bogus -- obviously so, because `path.root_dir.path` can |
| 502 | 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 | 577 | child.stderr_behavior = .Pipe; |
| 567 | 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 | 581 | .stdout = child.stdout.?, |
| 571 | 582 | .stderr = child.stderr.?, |
| 572 | 583 | }); |
| ... | ... | @@ -842,7 +853,10 @@ const cache_control_header: http.Header = .{ |
| 842 | 853 | }; |
| 843 | 854 | |
| 844 | 855 | const builtin = @import("builtin"); |
| 856 | ||
| 845 | 857 | const std = @import("std"); |
| 858 | const Io = std.Io; | |
| 859 | const net = std.Io.net; | |
| 846 | 860 | const assert = std.debug.assert; |
| 847 | 861 | const mem = std.mem; |
| 848 | 862 | const log = std.log.scoped(.web_server); |
| ... | ... | @@ -852,6 +866,5 @@ const Cache = Build.Cache; |
| 852 | 866 | const Fuzz = Build.Fuzz; |
| 853 | 867 | const abi = Build.abi; |
| 854 | 868 | const http = std.http; |
| 855 | const net = std.Io.net; | |
| 856 | 869 | |
| 857 | 870 | const WebServer = @This(); |
lib/std/Io.zig+12| ... | ... | @@ -654,6 +654,10 @@ pub const VTable = struct { |
| 654 | 654 | conditionWait: *const fn (?*anyopaque, cond: *Condition, mutex: *Mutex) Cancelable!void, |
| 655 | 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 | 661 | createFile: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File, |
| 658 | 662 | fileOpen: *const fn (?*anyopaque, dir: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File, |
| 659 | 663 | fileClose: *const fn (?*anyopaque, File) void, |
| ... | ... | @@ -804,6 +808,10 @@ pub const Timestamp = struct { |
| 804 | 808 | assert(lhs.clock == rhs.clock); |
| 805 | 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 | 817 | pub const Duration = struct { |
| ... | ... | @@ -831,6 +839,10 @@ pub const Duration = struct { |
| 831 | 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 | 846 | pub fn sleep(duration: Duration, io: Io) SleepError!void { |
| 835 | 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 | |
| 7 | 7 | handle: Handle, |
| 8 | 8 | |
| 9 | pub const Mode = Io.File.Mode; | |
| 10 | pub const default_mode: Mode = 0o755; | |
| 11 | ||
| 9 | 12 | pub fn cwd() Dir { |
| 10 | 13 | return .{ .handle = std.fs.cwd().fd }; |
| 11 | 14 | } |
| ... | ... | @@ -47,8 +50,9 @@ pub const UpdateFileError = File.OpenError; |
| 47 | 50 | |
| 48 | 51 | /// Check the file size, mtime, and mode of `source_path` and `dest_path`. If |
| 49 | 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 | |
| 51 | /// source file so that the next call to `updateFile` will not need a copy. | |
| 53 | /// `dest_path`, creating the parent directory hierarchy as needed. The | |
| 54 | /// destination file gains the mtime, atime, and mode of the source file so | |
| 55 | /// that the next call to `updateFile` will not need a copy. | |
| 52 | 56 | /// |
| 53 | 57 | /// Returns the previous status of the file before updating. |
| 54 | 58 | /// |
| ... | ... | @@ -65,7 +69,7 @@ pub fn updateFile( |
| 65 | 69 | options: std.fs.Dir.CopyFileOptions, |
| 66 | 70 | ) !PrevStatus { |
| 67 | 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 | 74 | const src_stat = try src_file.stat(io); |
| 71 | 75 | const actual_mode = options.override_mode orelse src_stat.mode; |
| ... | ... | @@ -93,13 +97,13 @@ pub fn updateFile( |
| 93 | 97 | } |
| 94 | 98 | |
| 95 | 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 | 101 | .mode = actual_mode, |
| 98 | 102 | .write_buffer = &buffer, |
| 99 | 103 | }); |
| 100 | 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 | 107 | const dest_writer = &atomic_file.file_writer.interface; |
| 104 | 108 | |
| 105 | 109 | _ = dest_writer.sendFileAll(&src_reader, .unlimited) catch |err| switch (err) { |
| ... | ... | @@ -111,3 +115,154 @@ pub fn updateFile( |
| 111 | 115 | try atomic_file.renameIntoPlace(); |
| 112 | 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 | 446 | |
| 447 | 447 | fn stream(io_reader: *Io.Reader, w: *Io.Writer, limit: Io.Limit) Io.Reader.StreamError!usize { |
| 448 | 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 | 454 | .positional, .streaming => return w.sendFile(r, limit) catch |write_err| switch (write_err) { |
| 451 | 455 | error.Unimplemented => { |
| 452 | 456 | r.mode = r.mode.toReading(); |
lib/std/Io/Threaded.zig+69-4| ... | ... | @@ -63,7 +63,17 @@ const Closure = struct { |
| 63 | 63 | |
| 64 | 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 | 77 | var pool: Pool = .{ |
| 68 | 78 | .allocator = gpa, |
| 69 | 79 | .threads = .empty, |
| ... | ... | @@ -77,6 +87,20 @@ pub fn init(gpa: Allocator) Pool { |
| 77 | 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 | 104 | pub fn deinit(pool: *Pool) void { |
| 81 | 105 | const gpa = pool.allocator; |
| 82 | 106 | pool.join(); |
| ... | ... | @@ -136,6 +160,10 @@ pub fn io(pool: *Pool) Io { |
| 136 | 160 | .conditionWait = conditionWait, |
| 137 | 161 | .conditionWake = conditionWake, |
| 138 | 162 | |
| 163 | .dirMake = dirMake, | |
| 164 | .dirStat = dirStat, | |
| 165 | .dirStatPath = dirStatPath, | |
| 166 | .fileStat = fileStat, | |
| 139 | 167 | .createFile = createFile, |
| 140 | 168 | .fileOpen = fileOpen, |
| 141 | 169 | .fileClose = fileClose, |
| ... | ... | @@ -520,10 +548,11 @@ fn groupAsync( |
| 520 | 548 | |
| 521 | 549 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { |
| 522 | 550 | const pool: *Pool = @ptrCast(@alignCast(userdata)); |
| 523 | _ = pool; | |
| 551 | const gpa = pool.allocator; | |
| 524 | 552 | |
| 525 | 553 | if (builtin.single_threaded) return; |
| 526 | 554 | |
| 555 | // TODO these primitives are too high level, need to check cancel on EINTR | |
| 527 | 556 | const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); |
| 528 | 557 | const reset_event: *ResetEvent = @ptrCast(&group.context); |
| 529 | 558 | std.Thread.WaitGroup.waitStateless(group_state, reset_event); |
| ... | ... | @@ -531,8 +560,9 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { |
| 531 | 560 | var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); |
| 532 | 561 | while (true) { |
| 533 | 562 | const gc: *GroupClosure = @fieldParentPtr("node", node); |
| 534 | gc.closure.requestCancel(); | |
| 535 | node = node.next orelse break; | |
| 563 | const node_next = node.next; | |
| 564 | gc.free(gpa); | |
| 565 | node = node_next orelse break; | |
| 536 | 566 | } |
| 537 | 567 | } |
| 538 | 568 | |
| ... | ... | @@ -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 | 792 | fn createFile( |
| 728 | 793 | userdata: ?*anyopaque, |
| 729 | 794 | dir: Io.Dir, |
lib/std/Io/Writer.zig+6-2| ... | ... | @@ -2827,6 +2827,8 @@ pub const Allocating = struct { |
| 2827 | 2827 | }; |
| 2828 | 2828 | |
| 2829 | 2829 | test "discarding sendFile" { |
| 2830 | const io = testing.io; | |
| 2831 | ||
| 2830 | 2832 | var tmp_dir = testing.tmpDir(.{}); |
| 2831 | 2833 | defer tmp_dir.cleanup(); |
| 2832 | 2834 | |
| ... | ... | @@ -2837,7 +2839,7 @@ test "discarding sendFile" { |
| 2837 | 2839 | try file_writer.interface.writeByte('h'); |
| 2838 | 2840 | try file_writer.interface.flush(); |
| 2839 | 2841 | |
| 2840 | var file_reader = file_writer.moveToReader(); | |
| 2842 | var file_reader = file_writer.moveToReader(io); | |
| 2841 | 2843 | try file_reader.seekTo(0); |
| 2842 | 2844 | |
| 2843 | 2845 | var w_buffer: [256]u8 = undefined; |
| ... | ... | @@ -2847,6 +2849,8 @@ test "discarding sendFile" { |
| 2847 | 2849 | } |
| 2848 | 2850 | |
| 2849 | 2851 | test "allocating sendFile" { |
| 2852 | const io = testing.io; | |
| 2853 | ||
| 2850 | 2854 | var tmp_dir = testing.tmpDir(.{}); |
| 2851 | 2855 | defer tmp_dir.cleanup(); |
| 2852 | 2856 | |
| ... | ... | @@ -2857,7 +2861,7 @@ test "allocating sendFile" { |
| 2857 | 2861 | try file_writer.interface.writeAll("abcd"); |
| 2858 | 2862 | try file_writer.interface.flush(); |
| 2859 | 2863 | |
| 2860 | var file_reader = file_writer.moveToReader(); | |
| 2864 | var file_reader = file_writer.moveToReader(io); | |
| 2861 | 2865 | try file_reader.seekTo(0); |
| 2862 | 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 | 57 | |
| 58 | 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 | 93 | /// Parse the given IP address string into an `IpAddress` value. |
| 61 | 94 | /// |
| 62 | 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 | 77 | InvalidDnsAAAARecord, |
| 78 | 78 | InvalidDnsCnameRecord, |
| 79 | 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 | 84 | pub const LookupResult = struct { |
| 83 | 85 | /// How many `LookupOptions.addresses_buffer` elements are populated. |
| ... | ... | @@ -428,14 +430,25 @@ fn lookupHosts(host_name: HostName, io: Io, options: LookupOptions) !LookupResul |
| 428 | 430 | error.AccessDenied, |
| 429 | 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 | 440 | defer file.close(io); |
| 434 | 441 | |
| 435 | 442 | var line_buf: [512]u8 = undefined; |
| 436 | 443 | var file_reader = file.reader(io, &line_buf); |
| 437 | 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 | 211 | const t = try std.Thread.spawn(.{}, S.clientFn, .{server.socket.address}); |
| 212 | 212 | defer t.join(); |
| 213 | 213 | |
| 214 | var client = try server.accept(io); | |
| 215 | defer client.stream.close(io); | |
| 214 | var stream = try server.accept(io); | |
| 215 | defer stream.close(io); | |
| 216 | 216 | var buf: [16]u8 = undefined; |
| 217 | var stream_reader = client.stream.reader(io, &.{}); | |
| 218 | const n = try stream_reader.interface().readSliceShort(&buf); | |
| 217 | var stream_reader = stream.reader(io, &.{}); | |
| 218 | const n = try stream_reader.interface.readSliceShort(&buf); | |
| 219 | 219 | |
| 220 | 220 | try testing.expectEqual(@as(usize, 12), n); |
| 221 | 221 | try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]); |
| ... | ... | @@ -267,10 +267,9 @@ fn testServer(server: *net.Server) anyerror!void { |
| 267 | 267 | |
| 268 | 268 | const io = testing.io; |
| 269 | 269 | |
| 270 | var client = try server.accept(io); | |
| 271 | ||
| 272 | const stream = client.stream.writer(io); | |
| 273 | try stream.print("hello from server\n", .{}); | |
| 270 | var stream = try server.accept(io); | |
| 271 | var writer = stream.writer(io, &.{}); | |
| 272 | try writer.interface.print("hello from server\n", .{}); | |
| 274 | 273 | } |
| 275 | 274 | |
| 276 | 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 | 309 | const t = try std.Thread.spawn(.{}, S.clientFn, .{socket_path}); |
| 311 | 310 | defer t.join(); |
| 312 | 311 | |
| 313 | var client = try server.accept(io); | |
| 314 | defer client.stream.close(io); | |
| 312 | var stream = try server.accept(io); | |
| 313 | defer stream.close(io); | |
| 315 | 314 | var buf: [16]u8 = undefined; |
| 316 | var stream_reader = client.stream.reader(io, &.{}); | |
| 317 | const n = try stream_reader.interface().readSliceShort(&buf); | |
| 315 | var stream_reader = stream.reader(io, &.{}); | |
| 316 | const n = try stream_reader.interface.readSliceShort(&buf); | |
| 318 | 317 | |
| 319 | 318 | try testing.expectEqual(@as(usize, 12), n); |
| 320 | 319 | try testing.expectEqualSlices(u8, "Hello world!", buf[0..n]); |
| ... | ... | @@ -366,10 +365,10 @@ test "non-blocking tcp server" { |
| 366 | 365 | const socket_file = try net.tcpConnectToAddress(server.socket.address); |
| 367 | 366 | defer socket_file.close(); |
| 368 | 367 | |
| 369 | var client = try server.accept(io); | |
| 370 | defer client.stream.close(io); | |
| 371 | const stream = client.stream.writer(io); | |
| 372 | try stream.print("hello from server\n", .{}); | |
| 368 | var stream = try server.accept(io); | |
| 369 | defer stream.close(io); | |
| 370 | var writer = stream.writer(io, .{}); | |
| 371 | try writer.interface.print("hello from server\n", .{}); | |
| 373 | 372 | |
| 374 | 373 | var buf: [100]u8 = undefined; |
| 375 | 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 | 105 | /// Verify that the server certificate is authorized by a given ca bundle. |
| 106 | 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 | 116 | /// If non-null, ssl secrets are logged to this stream. Creating such a log file allows |
| 109 | 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 | 128 | /// application layer itself verifies that the amount of data received equals |
| 121 | 129 | /// the amount of data expected, such as HTTP with the Content-Length header. |
| 122 | 130 | allow_truncation_attacks: bool = false, |
| 123 | write_buffer: []u8, | |
| 124 | read_buffer: []u8, | |
| 125 | 131 | /// Populated when `error.TlsAlert` is returned from `init`. |
| 126 | 132 | alert: ?*tls.Alert = null, |
| 127 | 133 | }; |
| ... | ... | @@ -189,14 +195,12 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 189 | 195 | }; |
| 190 | 196 | const host_len: u16 = @intCast(host.len); |
| 191 | 197 | |
| 192 | var random_buffer: [176]u8 = undefined; | |
| 193 | crypto.random.bytes(&random_buffer); | |
| 194 | const client_hello_rand = random_buffer[0..32].*; | |
| 198 | const client_hello_rand = options.entropy[0..32].*; | |
| 195 | 199 | var key_seq: u64 = 0; |
| 196 | 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 | 204 | // Only possible to happen if the seed is all zeroes. |
| 201 | 205 | error.IdentityElement => return error.InsufficientEntropy, |
| 202 | 206 | }; |
| ... | ... | @@ -321,7 +325,7 @@ pub fn init(input: *Reader, output: *Writer, options: Options) InitError!Client |
| 321 | 325 | var handshake_cipher: tls.HandshakeCipher = undefined; |
| 322 | 326 | var main_cert_pub_key: CertificatePublicKey = undefined; |
| 323 | 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 | 330 | var cleartext_fragment_start: usize = 0; |
| 327 | 331 | var cleartext_fragment_end: usize = 0; |
lib/std/debug/SelfInfo/Windows.zig+2-1| ... | ... | @@ -434,7 +434,7 @@ const Module = struct { |
| 434 | 434 | }; |
| 435 | 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 | 438 | pdb_reader.* = pdb_file.reader(try arena.alloc(u8, 4096)); |
| 439 | 439 | |
| 440 | 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 | 544 | } |
| 545 | 545 | |
| 546 | 546 | const std = @import("std"); |
| 547 | const Io = std.Io; | |
| 547 | 548 | const Allocator = std.mem.Allocator; |
| 548 | 549 | const Dwarf = std.debug.Dwarf; |
| 549 | 550 | const Pdb = std.debug.Pdb; |
lib/std/elf.zig+6-6| ... | ... | @@ -710,7 +710,7 @@ pub const ProgramHeaderIterator = struct { |
| 710 | 710 | const offset = it.phoff + size * it.index; |
| 711 | 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 | 731 | const offset = it.phoff + size * it.index; |
| 732 | 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 | 771 | const offset = it.shoff + size * it.index; |
| 772 | 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 | 793 | if (offset > it.buf.len) return error.EndOfStream; |
| 794 | 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 | 826 | |
| 827 | 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 | 830 | if (it.offset >= it.end_offset) return null; |
| 831 | 831 | const size: u64 = if (it.is_64) @sizeOf(Elf64_Dyn) else @sizeOf(Elf32_Dyn); |
| 832 | 832 | defer it.offset += size; |
| 833 | 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 | //! Deprecated in favor of `Io.Dir`. | |
| 1 | 2 | const Dir = @This(); |
| 3 | ||
| 2 | 4 | const builtin = @import("builtin"); |
| 5 | const native_os = builtin.os.tag; | |
| 6 | ||
| 3 | 7 | const std = @import("../std.zig"); |
| 8 | const Io = std.Io; | |
| 4 | 9 | const File = std.fs.File; |
| 5 | 10 | const AtomicFile = std.fs.AtomicFile; |
| 6 | 11 | const base64_encoder = fs.base64_encoder; |
| ... | ... | @@ -12,7 +17,6 @@ const Allocator = std.mem.Allocator; |
| 12 | 17 | const assert = std.debug.assert; |
| 13 | 18 | const linux = std.os.linux; |
| 14 | 19 | const windows = std.os.windows; |
| 15 | const native_os = builtin.os.tag; | |
| 16 | 20 | const have_flock = @TypeOf(posix.system.flock) != void; |
| 17 | 21 | |
| 18 | 22 | fd: Handle, |
| ... | ... | @@ -1189,84 +1193,41 @@ pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) |
| 1189 | 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. | |
| 1195 | /// To create multiple directories to make an entire path, see `makePath`. | |
| 1196 | /// To operate on only absolute paths, see `makeDirAbsolute`. | |
| 1197 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | |
| 1198 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | |
| 1199 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. | |
| 1199 | /// Deprecated in favor of `Io.Dir.makeDir`. | |
| 1200 | 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. | |
| 1205 | /// To create multiple directories to make an entire path, see `makePath`. | |
| 1206 | /// To operate on only absolute paths, see `makeDirAbsoluteZ`. | |
| 1206 | /// Deprecated in favor of `Io.Dir.makeDir`. | |
| 1207 | 1207 | pub fn makeDirZ(self: Dir, sub_path: [*:0]const u8) MakeError!void { |
| 1208 | 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. | |
| 1212 | /// To create multiple directories to make an entire path, see `makePath`. | |
| 1213 | /// To operate on only absolute paths, see `makeDirAbsoluteW`. | |
| 1211 | /// Deprecated in favor of `Io.Dir.makeDir`. | |
| 1214 | 1212 | pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) MakeError!void { |
| 1215 | 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 | |
| 1219 | /// (i.e. creating any parent directories that do not exist). | |
| 1220 | /// Returns success if the path already exists and is a directory. | |
| 1221 | /// This function is not atomic, and if it returns an error, the file system may | |
| 1222 | /// have been modified regardless. | |
| 1223 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | |
| 1224 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | |
| 1225 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. | |
| 1226 | /// Fails on an empty path with `error.BadPathName` as that is not a path that can be created. | |
| 1227 | /// | |
| 1228 | /// Paths containing `..` components are handled differently depending on the platform: | |
| 1229 | /// - On Windows, `..` are resolved before the path is passed to NtCreateFile, meaning | |
| 1230 | /// a `sub_path` like "first/../second" will resolve to "second" and only a | |
| 1231 | /// `./second` directory will be created. | |
| 1232 | /// - On other platforms, `..` are not resolved before the path is passed to `mkdirat`, | |
| 1233 | /// meaning a `sub_path` like "first/../second" will create both a `./first` | |
| 1234 | /// and a `./second` directory. | |
| 1235 | pub fn makePath(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!void { | |
| 1216 | /// Deprecated in favor of `Io.Dir.makePath`. | |
| 1217 | pub fn makePath(self: Dir, sub_path: []const u8) MakePathError!void { | |
| 1236 | 1218 | _ = try self.makePathStatus(sub_path); |
| 1237 | 1219 | } |
| 1238 | 1220 | |
| 1239 | pub const MakePathStatus = enum { existed, created }; | |
| 1240 | /// Same as `makePath` except returns whether the path already existed or was successfully created. | |
| 1241 | pub fn makePathStatus(self: Dir, sub_path: []const u8) (MakeError || StatFileError)!MakePathStatus { | |
| 1242 | var it = try fs.path.componentIterator(sub_path); | |
| 1243 | var status: MakePathStatus = .existed; | |
| 1244 | var component = it.last() orelse return error.BadPathName; | |
| 1245 | while (true) { | |
| 1246 | if (self.makeDir(component.path)) |_| { | |
| 1247 | status = .created; | |
| 1248 | } else |err| switch (err) { | |
| 1249 | error.PathAlreadyExists => { | |
| 1250 | // stat the file and return an error if it's not a directory | |
| 1251 | // this is important because otherwise a dangling symlink | |
| 1252 | // could cause an infinite loop | |
| 1253 | check_dir: { | |
| 1254 | // workaround for windows, see https://github.com/ziglang/zig/issues/16738 | |
| 1255 | const fstat = self.statFile(component.path) catch |stat_err| switch (stat_err) { | |
| 1256 | error.IsDir => break :check_dir, | |
| 1257 | else => |e| return e, | |
| 1258 | }; | |
| 1259 | if (fstat.kind != .directory) return error.NotDir; | |
| 1260 | } | |
| 1261 | }, | |
| 1262 | error.FileNotFound => |e| { | |
| 1263 | component = it.previous() orelse return e; | |
| 1264 | continue; | |
| 1265 | }, | |
| 1266 | else => |e| return e, | |
| 1267 | } | |
| 1268 | component = it.next() orelse return status; | |
| 1269 | } | |
| 1221 | /// Deprecated in favor of `Io.Dir.MakePathStatus`. | |
| 1222 | pub const MakePathStatus = Io.Dir.MakePathStatus; | |
| 1223 | /// Deprecated in favor of `Io.Dir.MakePathError`. | |
| 1224 | pub const MakePathError = Io.Dir.MakePathError; | |
| 1225 | ||
| 1226 | /// Deprecated in favor of `Io.Dir.makePathStatus`. | |
| 1227 | pub fn makePathStatus(self: Dir, sub_path: []const u8) MakePathError!MakePathStatus { | |
| 1228 | var threaded: Io.Threaded = .init_single_threaded; | |
| 1229 | const io = threaded.io(); | |
| 1230 | return Io.Dir.makePathStatus(.{ .handle = self.fd }, io, sub_path); | |
| 1270 | 1231 | } |
| 1271 | 1232 | |
| 1272 | 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 | 2013 | return windows.ReadLink(self.fd, sub_path_w, buffer); |
| 2053 | 2014 | } |
| 2054 | 2015 | |
| 2055 | /// Read all of file contents using a preallocated buffer. | |
| 2056 | /// The returned slice has the same pointer as `buffer`. If the length matches `buffer.len` | |
| 2057 | /// the situation is ambiguous. It could either mean that the entire file was read, and | |
| 2058 | /// it exactly fits the buffer, or it could mean the buffer was not big enough for the | |
| 2059 | /// entire file. | |
| 2060 | /// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | |
| 2061 | /// On WASI, `file_path` should be encoded as valid UTF-8. | |
| 2062 | /// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding. | |
| 2016 | /// Deprecated in favor of `Io.Dir.readFile`. | |
| 2063 | 2017 | pub fn readFile(self: Dir, file_path: []const u8, buffer: []u8) ![]u8 { |
| 2064 | var file = try self.openFile(file_path, .{}); | |
| 2065 | defer file.close(); | |
| 2066 | ||
| 2067 | const end_index = try file.readAll(buffer); | |
| 2068 | return buffer[0..end_index]; | |
| 2018 | var threaded: Io.Threaded = .init_single_threaded; | |
| 2019 | const io = threaded.io(); | |
| 2020 | return Io.Dir.readFile(.{ .handle = self.fd }, io, file_path, buffer); | |
| 2069 | 2021 | } |
| 2070 | 2022 | |
| 2071 | 2023 | pub const ReadFileAllocError = File.OpenError || File.ReadError || Allocator.Error || error{ |
| ... | ... | @@ -2091,7 +2043,7 @@ pub fn readFileAlloc( |
| 2091 | 2043 | /// Used to allocate the result. |
| 2092 | 2044 | gpa: Allocator, |
| 2093 | 2045 | /// If reached or exceeded, `error.StreamTooLong` is returned instead. |
| 2094 | limit: std.Io.Limit, | |
| 2046 | limit: Io.Limit, | |
| 2095 | 2047 | ) ReadFileAllocError![]u8 { |
| 2096 | 2048 | return readFileAllocOptions(dir, sub_path, gpa, limit, .of(u8), null); |
| 2097 | 2049 | } |
| ... | ... | @@ -2101,6 +2053,8 @@ pub fn readFileAlloc( |
| 2101 | 2053 | /// |
| 2102 | 2054 | /// If the file size is already known, a better alternative is to initialize a |
| 2103 | 2055 | /// `File.Reader`. |
| 2056 | /// | |
| 2057 | /// TODO move this function to Io.Dir | |
| 2104 | 2058 | pub fn readFileAllocOptions( |
| 2105 | 2059 | dir: Dir, |
| 2106 | 2060 | /// On Windows, should be encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| ... | ... | @@ -2110,13 +2064,16 @@ pub fn readFileAllocOptions( |
| 2110 | 2064 | /// Used to allocate the result. |
| 2111 | 2065 | gpa: Allocator, |
| 2112 | 2066 | /// If reached or exceeded, `error.StreamTooLong` is returned instead. |
| 2113 | limit: std.Io.Limit, | |
| 2067 | limit: Io.Limit, | |
| 2114 | 2068 | comptime alignment: std.mem.Alignment, |
| 2115 | 2069 | comptime sentinel: ?u8, |
| 2116 | 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 | 2074 | var file = try dir.openFile(sub_path, .{}); |
| 2118 | 2075 | defer file.close(); |
| 2119 | var file_reader = file.reader(&.{}); | |
| 2076 | var file_reader = file.reader(io, &.{}); | |
| 2120 | 2077 | return file_reader.interface.allocRemainingAlignedSentinel(gpa, limit, alignment, sentinel) catch |err| switch (err) { |
| 2121 | 2078 | error.ReadFailed => return file_reader.err.?, |
| 2122 | 2079 | error.OutOfMemory, error.StreamTooLong => |e| return e, |
| ... | ... | @@ -2647,6 +2604,8 @@ pub const CopyFileError = File.OpenError || File.StatError || |
| 2647 | 2604 | /// [WTF-8](https://wtf-8.codeberg.page/). On WASI, both paths should be |
| 2648 | 2605 | /// encoded as valid UTF-8. On other platforms, both paths are an opaque |
| 2649 | 2606 | /// sequence of bytes with no particular encoding. |
| 2607 | /// | |
| 2608 | /// TODO move this function to Io.Dir | |
| 2650 | 2609 | pub fn copyFile( |
| 2651 | 2610 | source_dir: Dir, |
| 2652 | 2611 | source_path: []const u8, |
| ... | ... | @@ -2654,11 +2613,15 @@ pub fn copyFile( |
| 2654 | 2613 | dest_path: []const u8, |
| 2655 | 2614 | options: CopyFileOptions, |
| 2656 | 2615 | ) CopyFileError!void { |
| 2657 | var file_reader: File.Reader = .init(try source_dir.openFile(source_path, .{}), &.{}); | |
| 2658 | defer file_reader.file.close(); | |
| 2616 | var threaded: Io.Threaded = .init_single_threaded; | |
| 2617 | const io = threaded.io(); | |
| 2618 | ||
| 2619 | const file = try source_dir.openFile(source_path, .{}); | |
| 2620 | var file_reader: File.Reader = .init(.{ .handle = file.handle }, io, &.{}); | |
| 2621 | defer file_reader.file.close(io); | |
| 2659 | 2622 | |
| 2660 | 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 | 2625 | file_reader.size = st.size; |
| 2663 | 2626 | break :blk st.mode; |
| 2664 | 2627 | }; |
| ... | ... | @@ -2708,6 +2671,7 @@ pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) |
| 2708 | 2671 | pub const Stat = File.Stat; |
| 2709 | 2672 | pub const StatError = File.StatError; |
| 2710 | 2673 | |
| 2674 | /// Deprecated in favor of `Io.Dir.stat`. | |
| 2711 | 2675 | pub fn stat(self: Dir) StatError!Stat { |
| 2712 | 2676 | const file: File = .{ .handle = self.fd }; |
| 2713 | 2677 | return file.stat(); |
| ... | ... | @@ -2715,17 +2679,7 @@ pub fn stat(self: Dir) StatError!Stat { |
| 2715 | 2679 | |
| 2716 | 2680 | pub const StatFileError = File.OpenError || File.StatError || posix.FStatAtError; |
| 2717 | 2681 | |
| 2718 | /// Returns metadata for a file inside the directory. | |
| 2719 | /// | |
| 2720 | /// On Windows, this requires three syscalls. On other operating systems, it | |
| 2721 | /// only takes one. | |
| 2722 | /// | |
| 2723 | /// Symlinks are followed. | |
| 2724 | /// | |
| 2725 | /// `sub_path` may be absolute, in which case `self` is ignored. | |
| 2726 | /// On Windows, `sub_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | |
| 2727 | /// On WASI, `sub_path` should be encoded as valid UTF-8. | |
| 2728 | /// On other platforms, `sub_path` is an opaque sequence of bytes with no particular encoding. | |
| 2682 | /// Deprecated in favor of `Io.Dir.statPath`. | |
| 2729 | 2683 | pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat { |
| 2730 | 2684 | if (native_os == .windows) { |
| 2731 | 2685 | var file = try self.openFile(sub_path, .{}); |
| ... | ... | @@ -2799,3 +2753,11 @@ pub fn setPermissions(self: Dir, permissions: Permissions) SetPermissionsError!v |
| 2799 | 2753 | const file: File = .{ .handle = self.fd }; |
| 2800 | 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 | 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 | 863 | defer w.* = undefined; |
| 863 | 864 | return .{ |
| 864 | .file = w.file, | |
| 865 | .io = io, | |
| 866 | .file = .{ .handle = w.file.handle }, | |
| 865 | 867 | .mode = w.mode, |
| 866 | 868 | .pos = w.pos, |
| 867 | 869 | .interface = Reader.initInterface(w.interface.buffer), |
| ... | ... | @@ -1350,15 +1352,15 @@ pub const Writer = struct { |
| 1350 | 1352 | /// |
| 1351 | 1353 | /// Positional is more threadsafe, since the global seek position is not |
| 1352 | 1354 | /// affected. |
| 1353 | pub fn reader(file: File, buffer: []u8) Reader { | |
| 1354 | return .init(file, buffer); | |
| 1355 | pub fn reader(file: File, io: std.Io, buffer: []u8) Reader { | |
| 1356 | return .init(.{ .handle = file.handle }, io, buffer); | |
| 1355 | 1357 | } |
| 1356 | 1358 | |
| 1357 | 1359 | /// Positional is more threadsafe, since the global seek position is not |
| 1358 | 1360 | /// affected, but when such syscalls are not available, preemptively |
| 1359 | 1361 | /// initializing in streaming mode skips a failed syscall. |
| 1360 | pub fn readerStreaming(file: File, buffer: []u8) Reader { | |
| 1361 | return .initStreaming(file, buffer); | |
| 1362 | pub fn readerStreaming(file: File, io: std.Io, buffer: []u8) Reader { | |
| 1363 | return .initStreaming(.{ .handle = file.handle }, io, buffer); | |
| 1362 | 1364 | } |
| 1363 | 1365 | |
| 1364 | 1366 | /// Defaults to positional reading; falls back to streaming. |
| ... | ... | @@ -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 | const std = @import("../std.zig"); | |
| 2 | 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 | 6 | const testing = std.testing; |
| 4 | 7 | const fs = std.fs; |
| 5 | 8 | const mem = std.mem; |
| 6 | 9 | const wasi = std.os.wasi; |
| 7 | const native_os = builtin.os.tag; | |
| 8 | 10 | const windows = std.os.windows; |
| 9 | 11 | const posix = std.posix; |
| 10 | 12 | |
| ... | ... | @@ -73,6 +75,7 @@ const PathType = enum { |
| 73 | 75 | }; |
| 74 | 76 | |
| 75 | 77 | const TestContext = struct { |
| 78 | io: Io, | |
| 76 | 79 | path_type: PathType, |
| 77 | 80 | path_sep: u8, |
| 78 | 81 | arena: ArenaAllocator, |
| ... | ... | @@ -83,6 +86,7 @@ const TestContext = struct { |
| 83 | 86 | pub fn init(path_type: PathType, path_sep: u8, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext { |
| 84 | 87 | const tmp = tmpDir(.{ .iterate = true }); |
| 85 | 88 | return .{ |
| 89 | .io = testing.io, | |
| 86 | 90 | .path_type = path_type, |
| 87 | 91 | .path_sep = path_sep, |
| 88 | 92 | .arena = ArenaAllocator.init(allocator), |
| ... | ... | @@ -1319,6 +1323,8 @@ test "max file name component lengths" { |
| 1319 | 1323 | } |
| 1320 | 1324 | |
| 1321 | 1325 | test "writev, readv" { |
| 1326 | const io = testing.io; | |
| 1327 | ||
| 1322 | 1328 | var tmp = tmpDir(.{}); |
| 1323 | 1329 | defer tmp.cleanup(); |
| 1324 | 1330 | |
| ... | ... | @@ -1327,78 +1333,55 @@ test "writev, readv" { |
| 1327 | 1333 | |
| 1328 | 1334 | var buf1: [line1.len]u8 = undefined; |
| 1329 | 1335 | var buf2: [line2.len]u8 = undefined; |
| 1330 | var write_vecs = [_]posix.iovec_const{ | |
| 1331 | .{ | |
| 1332 | .base = line1, | |
| 1333 | .len = line1.len, | |
| 1334 | }, | |
| 1335 | .{ | |
| 1336 | .base = line2, | |
| 1337 | .len = line2.len, | |
| 1338 | }, | |
| 1339 | }; | |
| 1340 | var read_vecs = [_]posix.iovec{ | |
| 1341 | .{ | |
| 1342 | .base = &buf2, | |
| 1343 | .len = buf2.len, | |
| 1344 | }, | |
| 1345 | .{ | |
| 1346 | .base = &buf1, | |
| 1347 | .len = buf1.len, | |
| 1348 | }, | |
| 1349 | }; | |
| 1336 | var write_vecs: [2][]const u8 = .{ line1, line2 }; | |
| 1337 | var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; | |
| 1350 | 1338 | |
| 1351 | 1339 | var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); |
| 1352 | 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 | 1346 | try testing.expectEqual(@as(u64, line1.len + line2.len), try src_file.getEndPos()); |
| 1356 | try src_file.seekTo(0); | |
| 1357 | const read = try src_file.readvAll(&read_vecs); | |
| 1358 | try testing.expectEqual(@as(usize, line1.len + line2.len), read); | |
| 1347 | ||
| 1348 | var reader = writer.moveToReader(io); | |
| 1349 | try reader.seekTo(0); | |
| 1350 | try reader.interface.readVecAll(&read_vecs); | |
| 1359 | 1351 | try testing.expectEqualStrings(&buf1, "line2\n"); |
| 1360 | 1352 | try testing.expectEqualStrings(&buf2, "line1\n"); |
| 1353 | try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1)); | |
| 1361 | 1354 | } |
| 1362 | 1355 | |
| 1363 | 1356 | test "pwritev, preadv" { |
| 1357 | const io = testing.io; | |
| 1358 | ||
| 1364 | 1359 | var tmp = tmpDir(.{}); |
| 1365 | 1360 | defer tmp.cleanup(); |
| 1366 | 1361 | |
| 1367 | 1362 | const line1 = "line1\n"; |
| 1368 | 1363 | const line2 = "line2\n"; |
| 1369 | ||
| 1364 | var lines: [2][]const u8 = .{ line1, line2 }; | |
| 1370 | 1365 | var buf1: [line1.len]u8 = undefined; |
| 1371 | 1366 | var buf2: [line2.len]u8 = undefined; |
| 1372 | var write_vecs = [_]posix.iovec_const{ | |
| 1373 | .{ | |
| 1374 | .base = line1, | |
| 1375 | .len = line1.len, | |
| 1376 | }, | |
| 1377 | .{ | |
| 1378 | .base = line2, | |
| 1379 | .len = line2.len, | |
| 1380 | }, | |
| 1381 | }; | |
| 1382 | var read_vecs = [_]posix.iovec{ | |
| 1383 | .{ | |
| 1384 | .base = &buf2, | |
| 1385 | .len = buf2.len, | |
| 1386 | }, | |
| 1387 | .{ | |
| 1388 | .base = &buf1, | |
| 1389 | .len = buf1.len, | |
| 1390 | }, | |
| 1391 | }; | |
| 1367 | var read_vecs: [2][]u8 = .{ &buf2, &buf1 }; | |
| 1392 | 1368 | |
| 1393 | 1369 | var src_file = try tmp.dir.createFile("test.txt", .{ .read = true }); |
| 1394 | 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 | 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); | |
| 1399 | try testing.expectEqual(@as(usize, line1.len + line2.len), read); | |
| 1378 | ||
| 1379 | var reader = writer.moveToReader(io); | |
| 1380 | try reader.seekTo(16); | |
| 1381 | try reader.interface.readVecAll(&read_vecs); | |
| 1400 | 1382 | try testing.expectEqualStrings(&buf1, "line2\n"); |
| 1401 | 1383 | try testing.expectEqualStrings(&buf2, "line1\n"); |
| 1384 | try testing.expectError(error.EndOfStream, reader.interface.readSliceAll(&buf1)); | |
| 1402 | 1385 | } |
| 1403 | 1386 | |
| 1404 | 1387 | test "setEndPos" { |
| ... | ... | @@ -1406,6 +1389,8 @@ test "setEndPos" { |
| 1406 | 1389 | if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; |
| 1407 | 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 | 1394 | var tmp = tmpDir(.{}); |
| 1410 | 1395 | defer tmp.cleanup(); |
| 1411 | 1396 | |
| ... | ... | @@ -1416,11 +1401,13 @@ test "setEndPos" { |
| 1416 | 1401 | |
| 1417 | 1402 | const initial_size = try f.getEndPos(); |
| 1418 | 1403 | var buffer: [32]u8 = undefined; |
| 1404 | var reader = f.reader(io, &.{}); | |
| 1419 | 1405 | |
| 1420 | 1406 | { |
| 1421 | 1407 | try f.setEndPos(initial_size); |
| 1422 | 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 | 1411 | try testing.expectEqualStrings("ninebytes", buffer[0..@intCast(initial_size)]); |
| 1425 | 1412 | } |
| 1426 | 1413 | |
| ... | ... | @@ -1428,7 +1415,8 @@ test "setEndPos" { |
| 1428 | 1415 | const larger = initial_size + 4; |
| 1429 | 1416 | try f.setEndPos(larger); |
| 1430 | 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 | 1420 | try testing.expectEqualStrings("ninebytes\x00\x00\x00\x00", buffer[0..@intCast(larger)]); |
| 1433 | 1421 | } |
| 1434 | 1422 | |
| ... | ... | @@ -1436,25 +1424,21 @@ test "setEndPos" { |
| 1436 | 1424 | const smaller = initial_size - 5; |
| 1437 | 1425 | try f.setEndPos(smaller); |
| 1438 | 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 | 1429 | try testing.expectEqualStrings("nine", buffer[0..@intCast(smaller)]); |
| 1441 | 1430 | } |
| 1442 | 1431 | |
| 1443 | 1432 | try f.setEndPos(0); |
| 1444 | 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 | 1437 | // Invalid file length should error gracefully. Actual limit is host |
| 1448 | 1438 | // and file-system dependent, but 1PB should fail on filesystems like |
| 1449 | 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) { | |
| 1451 | return err; | |
| 1452 | }; | |
| 1453 | ||
| 1454 | f.setEndPos(std.math.maxInt(u63)) catch |err| if (err != error.FileTooBig) { | |
| 1455 | return err; | |
| 1456 | }; | |
| 1457 | ||
| 1440 | try testing.expectError(error.FileTooBig, f.setEndPos(0x4_0000_0000_0000)); | |
| 1441 | try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63))); | |
| 1458 | 1442 | try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u63) + 1)); |
| 1459 | 1443 | try testing.expectError(error.FileTooBig, f.setEndPos(std.math.maxInt(u64))); |
| 1460 | 1444 | } |
| ... | ... | @@ -1560,31 +1544,6 @@ test "sendfile with buffered data" { |
| 1560 | 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 | 1547 | test "copyFile" { |
| 1589 | 1548 | try testWithAllSupportedPathTypes(struct { |
| 1590 | 1549 | fn impl(ctx: *TestContext) !void { |
| ... | ... | @@ -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{}; | |
| 1712 | var locked = std.Thread.ResetEvent{}; | |
| 1670 | var started: std.Thread.ResetEvent = .unset; | |
| 1671 | var locked: std.Thread.ResetEvent = .unset; | |
| 1713 | 1672 | |
| 1714 | 1673 | const t = try std.Thread.spawn(.{}, S.checkFn, .{ |
| 1715 | 1674 | &ctx.dir, |
| ... | ... | @@ -1773,7 +1732,7 @@ test "read from locked file" { |
| 1773 | 1732 | const f = try ctx.dir.createFile(filename, .{ .read = true }); |
| 1774 | 1733 | defer f.close(); |
| 1775 | 1734 | var buffer: [1]u8 = undefined; |
| 1776 | _ = try f.readAll(&buffer); | |
| 1735 | _ = try f.read(&buffer); | |
| 1777 | 1736 | } |
| 1778 | 1737 | { |
| 1779 | 1738 | const f = try ctx.dir.createFile(filename, .{ |
| ... | ... | @@ -1785,9 +1744,9 @@ test "read from locked file" { |
| 1785 | 1744 | defer f2.close(); |
| 1786 | 1745 | var buffer: [1]u8 = undefined; |
| 1787 | 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 | 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 | 1903 | |
| 1945 | 1904 | try testWithAllSupportedPathTypes(struct { |
| 1946 | 1905 | fn impl(ctx: *TestContext) !void { |
| 1906 | const io = ctx.io; | |
| 1947 | 1907 | const subdir_path = try ctx.transformPath("./subdir"); |
| 1948 | 1908 | const file_path = try ctx.transformPath("./subdir/../file"); |
| 1949 | 1909 | const copy_path = try ctx.transformPath("./subdir/../copy"); |
| ... | ... | @@ -1966,7 +1926,8 @@ test "'.' and '..' in fs.Dir functions" { |
| 1966 | 1926 | try ctx.dir.deleteFile(rename_path); |
| 1967 | 1927 | |
| 1968 | 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 | 1931 | try testing.expectEqual(fs.Dir.PrevStatus.stale, prev_status); |
| 1971 | 1932 | |
| 1972 | 1933 | try ctx.dir.deleteDir(subdir_path); |
| ... | ... | @@ -2005,13 +1966,6 @@ test "'.' and '..' in absolute functions" { |
| 2005 | 1966 | renamed_file.close(); |
| 2006 | 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 | 1969 | try fs.deleteDirAbsolute(subdir_path); |
| 2016 | 1970 | } |
| 2017 | 1971 | |
| ... | ... | @@ -2079,6 +2033,7 @@ test "invalid UTF-8/WTF-8 paths" { |
| 2079 | 2033 | |
| 2080 | 2034 | try testWithAllSupportedPathTypes(struct { |
| 2081 | 2035 | fn impl(ctx: *TestContext) !void { |
| 2036 | const io = ctx.io; | |
| 2082 | 2037 | // This is both invalid UTF-8 and WTF-8, since \xFF is an invalid start byte |
| 2083 | 2038 | const invalid_path = try ctx.transformPath("\xFF"); |
| 2084 | 2039 | |
| ... | ... | @@ -2129,7 +2084,8 @@ test "invalid UTF-8/WTF-8 paths" { |
| 2129 | 2084 | try testing.expectError(expected_err, ctx.dir.access(invalid_path, .{})); |
| 2130 | 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 | 2089 | try testing.expectError(expected_err, ctx.dir.copyFile(invalid_path, ctx.dir, invalid_path, .{})); |
| 2134 | 2090 | |
| 2135 | 2091 | try testing.expectError(expected_err, ctx.dir.statFile(invalid_path)); |
| ... | ... | @@ -2144,7 +2100,6 @@ test "invalid UTF-8/WTF-8 paths" { |
| 2144 | 2100 | try testing.expectError(expected_err, fs.renameZ(ctx.dir, invalid_path, ctx.dir, invalid_path)); |
| 2145 | 2101 | |
| 2146 | 2102 | if (native_os != .wasi and ctx.path_type != .relative) { |
| 2147 | try testing.expectError(expected_err, fs.updateFileAbsolute(invalid_path, invalid_path, .{})); | |
| 2148 | 2103 | try testing.expectError(expected_err, fs.copyFileAbsolute(invalid_path, invalid_path, .{})); |
| 2149 | 2104 | try testing.expectError(expected_err, fs.makeDirAbsolute(invalid_path)); |
| 2150 | 2105 | try testing.expectError(expected_err, fs.makeDirAbsoluteZ(invalid_path)); |
| ... | ... | @@ -2175,6 +2130,8 @@ test "invalid UTF-8/WTF-8 paths" { |
| 2175 | 2130 | } |
| 2176 | 2131 | |
| 2177 | 2132 | test "read file non vectored" { |
| 2133 | const io = std.testing.io; | |
| 2134 | ||
| 2178 | 2135 | var tmp_dir = testing.tmpDir(.{}); |
| 2179 | 2136 | defer tmp_dir.cleanup(); |
| 2180 | 2137 | |
| ... | ... | @@ -2188,7 +2145,7 @@ test "read file non vectored" { |
| 2188 | 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 | 2150 | var write_buffer: [100]u8 = undefined; |
| 2194 | 2151 | var w: std.Io.Writer = .fixed(&write_buffer); |
| ... | ... | @@ -2205,6 +2162,8 @@ test "read file non vectored" { |
| 2205 | 2162 | } |
| 2206 | 2163 | |
| 2207 | 2164 | test "seek keeping partial buffer" { |
| 2165 | const io = std.testing.io; | |
| 2166 | ||
| 2208 | 2167 | var tmp_dir = testing.tmpDir(.{}); |
| 2209 | 2168 | defer tmp_dir.cleanup(); |
| 2210 | 2169 | |
| ... | ... | @@ -2219,7 +2178,7 @@ test "seek keeping partial buffer" { |
| 2219 | 2178 | } |
| 2220 | 2179 | |
| 2221 | 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 | 2183 | try testing.expectEqual(0, file_reader.logicalPos()); |
| 2225 | 2184 | |
| ... | ... | @@ -2246,13 +2205,15 @@ test "seek keeping partial buffer" { |
| 2246 | 2205 | } |
| 2247 | 2206 | |
| 2248 | 2207 | test "seekBy" { |
| 2208 | const io = testing.io; | |
| 2209 | ||
| 2249 | 2210 | var tmp_dir = testing.tmpDir(.{}); |
| 2250 | 2211 | defer tmp_dir.cleanup(); |
| 2251 | 2212 | |
| 2252 | 2213 | try tmp_dir.dir.writeFile(.{ .sub_path = "blah.txt", .data = "let's test seekBy" }); |
| 2253 | 2214 | const f = try tmp_dir.dir.openFile("blah.txt", .{ .mode = .read_only }); |
| 2254 | 2215 | defer f.close(); |
| 2255 | var reader = f.readerStreaming(&.{}); | |
| 2216 | var reader = f.readerStreaming(io, &.{}); | |
| 2256 | 2217 | try reader.seekBy(2); |
| 2257 | 2218 | |
| 2258 | 2219 | var buffer: [20]u8 = undefined; |
lib/std/http/Client.zig+20-25| ... | ... | @@ -247,6 +247,7 @@ pub const Connection = struct { |
| 247 | 247 | port: u16, |
| 248 | 248 | stream: Io.net.Stream, |
| 249 | 249 | ) error{OutOfMemory}!*Plain { |
| 250 | const io = client.io; | |
| 250 | 251 | const gpa = client.allocator; |
| 251 | 252 | const alloc_len = allocLen(client, remote_host.bytes.len); |
| 252 | 253 | const base = try gpa.alignedAlloc(u8, .of(Plain), alloc_len); |
| ... | ... | @@ -260,8 +261,8 @@ pub const Connection = struct { |
| 260 | 261 | plain.* = .{ |
| 261 | 262 | .connection = .{ |
| 262 | 263 | .client = client, |
| 263 | .stream_writer = stream.writer(socket_write_buffer), | |
| 264 | .stream_reader = stream.reader(socket_read_buffer), | |
| 264 | .stream_writer = stream.writer(io, socket_write_buffer), | |
| 265 | .stream_reader = stream.reader(io, socket_read_buffer), | |
| 265 | 266 | .pool_node = .{}, |
| 266 | 267 | .port = port, |
| 267 | 268 | .host_len = @intCast(remote_host.bytes.len), |
| ... | ... | @@ -300,6 +301,7 @@ pub const Connection = struct { |
| 300 | 301 | port: u16, |
| 301 | 302 | stream: Io.net.Stream, |
| 302 | 303 | ) error{ OutOfMemory, TlsInitializationFailed }!*Tls { |
| 304 | const io = client.io; | |
| 303 | 305 | const gpa = client.allocator; |
| 304 | 306 | const alloc_len = allocLen(client, remote_host.bytes.len); |
| 305 | 307 | const base = try gpa.alignedAlloc(u8, .of(Tls), alloc_len); |
| ... | ... | @@ -316,11 +318,14 @@ pub const Connection = struct { |
| 316 | 318 | assert(base.ptr + alloc_len == socket_read_buffer.ptr + socket_read_buffer.len); |
| 317 | 319 | @memcpy(host_buffer, remote_host.bytes); |
| 318 | 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 | 324 | tls.* = .{ |
| 320 | 325 | .connection = .{ |
| 321 | 326 | .client = client, |
| 322 | .stream_writer = stream.writer(tls_write_buffer), | |
| 323 | .stream_reader = stream.reader(socket_read_buffer), | |
| 327 | .stream_writer = stream.writer(io, tls_write_buffer), | |
| 328 | .stream_reader = stream.reader(io, socket_read_buffer), | |
| 324 | 329 | .pool_node = .{}, |
| 325 | 330 | .port = port, |
| 326 | 331 | .host_len = @intCast(remote_host.bytes.len), |
| ... | ... | @@ -338,6 +343,8 @@ pub const Connection = struct { |
| 338 | 343 | .ssl_key_log = client.ssl_key_log, |
| 339 | 344 | .read_buffer = tls_read_buffer, |
| 340 | 345 | .write_buffer = socket_write_buffer, |
| 346 | .entropy = &random_buffer, | |
| 347 | .realtime_now_seconds = now_ts, | |
| 341 | 348 | // This is appropriate for HTTPS because the HTTP headers contain |
| 342 | 349 | // the content length which is used to detect truncation attacks. |
| 343 | 350 | .allow_truncation_attacks = true, |
| ... | ... | @@ -1390,16 +1397,8 @@ pub const basic_authorization = struct { |
| 1390 | 1397 | }; |
| 1391 | 1398 | |
| 1392 | 1399 | pub const ConnectTcpError = error{ |
| 1393 | ConnectionRefused, | |
| 1394 | NetworkUnreachable, | |
| 1395 | ConnectionTimedOut, | |
| 1396 | ConnectionResetByPeer, | |
| 1397 | TemporaryNameServerFailure, | |
| 1398 | NameServerFailure, | |
| 1399 | UnknownHostName, | |
| 1400 | UnexpectedConnectFailure, | |
| 1401 | 1400 | TlsInitializationFailed, |
| 1402 | } || Allocator.Error || Io.Cancelable; | |
| 1401 | } || Allocator.Error || HostName.ConnectError; | |
| 1403 | 1402 | |
| 1404 | 1403 | /// Reuses a `Connection` if one matching `host` and `port` is already open. |
| 1405 | 1404 | /// |
| ... | ... | @@ -1424,6 +1423,7 @@ pub const ConnectTcpOptions = struct { |
| 1424 | 1423 | }; |
| 1425 | 1424 | |
| 1426 | 1425 | pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcpError!*Connection { |
| 1426 | const io = client.io; | |
| 1427 | 1427 | const host = options.host; |
| 1428 | 1428 | const port = options.port; |
| 1429 | 1429 | const protocol = options.protocol; |
| ... | ... | @@ -1437,22 +1437,17 @@ pub fn connectTcpOptions(client: *Client, options: ConnectTcpOptions) ConnectTcp |
| 1437 | 1437 | .protocol = protocol, |
| 1438 | 1438 | })) |conn| return conn; |
| 1439 | 1439 | |
| 1440 | const stream = host.connect(client.io, port, .{ .mode = .stream }) catch |err| switch (err) { | |
| 1441 | error.ConnectionRefused => return error.ConnectionRefused, | |
| 1442 | error.NetworkUnreachable => return error.NetworkUnreachable, | |
| 1443 | error.ConnectionTimedOut => return error.ConnectionTimedOut, | |
| 1444 | error.ConnectionResetByPeer => return error.ConnectionResetByPeer, | |
| 1445 | error.NameServerFailure => return error.NameServerFailure, | |
| 1446 | error.UnknownHostName => return error.UnknownHostName, | |
| 1447 | error.Canceled => return error.Canceled, | |
| 1448 | //else => return error.UnexpectedConnectFailure, | |
| 1449 | }; | |
| 1450 | errdefer stream.close(); | |
| 1440 | var stream = try host.connect(io, port, .{ .mode = .stream }); | |
| 1441 | errdefer stream.close(io); | |
| 1451 | 1442 | |
| 1452 | 1443 | switch (protocol) { |
| 1453 | 1444 | .tls => { |
| 1454 | 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 | 1451 | client.connection_pool.addUsed(&tc.connection); |
| 1457 | 1452 | return &tc.connection; |
| 1458 | 1453 | }, |
lib/std/os/linux/IoUring.zig+90-72| ... | ... | @@ -3,7 +3,7 @@ const std = @import("std"); |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const mem = std.mem; |
| 6 | const net = std.net; | |
| 6 | const net = std.Io.net; | |
| 7 | 7 | const posix = std.posix; |
| 8 | 8 | const linux = std.os.linux; |
| 9 | 9 | const testing = std.testing; |
| ... | ... | @@ -2361,19 +2361,22 @@ test "sendmsg/recvmsg" { |
| 2361 | 2361 | }; |
| 2362 | 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 | 2370 | defer posix.close(server); |
| 2368 | 2371 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1))); |
| 2369 | 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 | 2375 | // set address_server to the OS-chosen IP/port. |
| 2373 | var slen: posix.socklen_t = address_server.getOsSockLen(); | |
| 2374 | try posix.getsockname(server, &address_server.any, &slen); | |
| 2376 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); | |
| 2377 | try posix.getsockname(server, addrAny(&address_server), &slen); | |
| 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 | 2380 | defer posix.close(client); |
| 2378 | 2381 | |
| 2379 | 2382 | const buffer_send = [_]u8{42} ** 128; |
| ... | ... | @@ -2381,8 +2384,8 @@ test "sendmsg/recvmsg" { |
| 2381 | 2384 | posix.iovec_const{ .base = &buffer_send, .len = buffer_send.len }, |
| 2382 | 2385 | }; |
| 2383 | 2386 | const msg_send: posix.msghdr_const = .{ |
| 2384 | .name = &address_server.any, | |
| 2385 | .namelen = address_server.getOsSockLen(), | |
| 2387 | .name = addrAny(&address_server), | |
| 2388 | .namelen = @sizeOf(linux.sockaddr.in), | |
| 2386 | 2389 | .iov = &iovecs_send, |
| 2387 | 2390 | .iovlen = 1, |
| 2388 | 2391 | .control = null, |
| ... | ... | @@ -2398,11 +2401,13 @@ test "sendmsg/recvmsg" { |
| 2398 | 2401 | var iovecs_recv = [_]posix.iovec{ |
| 2399 | 2402 | posix.iovec{ .base = &buffer_recv, .len = buffer_recv.len }, |
| 2400 | 2403 | }; |
| 2401 | const addr = [_]u8{0} ** 4; | |
| 2402 | var address_recv = net.Address.initIp4(addr, 0); | |
| 2404 | var address_recv: linux.sockaddr.in = .{ | |
| 2405 | .port = 0, | |
| 2406 | .addr = 0, | |
| 2407 | }; | |
| 2403 | 2408 | var msg_recv: posix.msghdr = .{ |
| 2404 | .name = &address_recv.any, | |
| 2405 | .namelen = address_recv.getOsSockLen(), | |
| 2409 | .name = addrAny(&address_recv), | |
| 2410 | .namelen = @sizeOf(linux.sockaddr.in), | |
| 2406 | 2411 | .iov = &iovecs_recv, |
| 2407 | 2412 | .iovlen = 1, |
| 2408 | 2413 | .control = null, |
| ... | ... | @@ -2441,6 +2446,8 @@ test "sendmsg/recvmsg" { |
| 2441 | 2446 | test "timeout (after a relative time)" { |
| 2442 | 2447 | if (!is_linux) return error.SkipZigTest; |
| 2443 | 2448 | |
| 2449 | const io = testing.io; | |
| 2450 | ||
| 2444 | 2451 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 2445 | 2452 | error.SystemOutdated => return error.SkipZigTest, |
| 2446 | 2453 | error.PermissionDenied => return error.SkipZigTest, |
| ... | ... | @@ -2452,12 +2459,12 @@ test "timeout (after a relative time)" { |
| 2452 | 2459 | const margin = 5; |
| 2453 | 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 | 2463 | const sqe = try ring.timeout(0x55555555, &ts, 0, 0); |
| 2457 | 2464 | try testing.expectEqual(linux.IORING_OP.TIMEOUT, sqe.opcode); |
| 2458 | 2465 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 2459 | 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 | 2469 | try testing.expectEqual(linux.io_uring_cqe{ |
| 2463 | 2470 | .user_data = 0x55555555, |
| ... | ... | @@ -2466,7 +2473,8 @@ test "timeout (after a relative time)" { |
| 2466 | 2473 | }, cqe); |
| 2467 | 2474 | |
| 2468 | 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 | 2480 | test "timeout (after a number of completions)" { |
| ... | ... | @@ -2861,19 +2869,22 @@ test "shutdown" { |
| 2861 | 2869 | }; |
| 2862 | 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 | 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 | 2880 | defer posix.close(server); |
| 2870 | 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 | 2883 | try posix.listen(server, 1); |
| 2873 | 2884 | |
| 2874 | 2885 | // set address to the OS-chosen IP/port. |
| 2875 | var slen: posix.socklen_t = address.getOsSockLen(); | |
| 2876 | try posix.getsockname(server, &address.any, &slen); | |
| 2886 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); | |
| 2887 | try posix.getsockname(server, addrAny(&address), &slen); | |
| 2877 | 2888 | |
| 2878 | 2889 | const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD); |
| 2879 | 2890 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| ... | ... | @@ -2898,7 +2909,7 @@ test "shutdown" { |
| 2898 | 2909 | |
| 2899 | 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 | 2913 | defer posix.close(server); |
| 2903 | 2914 | |
| 2904 | 2915 | const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) { |
| ... | ... | @@ -2966,22 +2977,11 @@ test "renameat" { |
| 2966 | 2977 | }, cqe); |
| 2967 | 2978 | |
| 2968 | 2979 | // Validate that the old file doesn't exist anymore |
| 2969 | { | |
| 2970 | _ = tmp.dir.openFile(old_path, .{}) catch |err| switch (err) { | |
| 2971 | error.FileNotFound => {}, | |
| 2972 | else => std.debug.panic("unexpected error: {}", .{err}), | |
| 2973 | }; | |
| 2974 | } | |
| 2980 | try testing.expectError(error.FileNotFound, tmp.dir.openFile(old_path, .{})); | |
| 2975 | 2981 | |
| 2976 | 2982 | // Validate that the new file exists with the proper content |
| 2977 | { | |
| 2978 | const new_file = try tmp.dir.openFile(new_path, .{}); | |
| 2979 | defer new_file.close(); | |
| 2980 | ||
| 2981 | var new_file_data: [16]u8 = undefined; | |
| 2982 | const bytes_read = try new_file.readAll(&new_file_data); | |
| 2983 | try testing.expectEqualStrings("hello", new_file_data[0..bytes_read]); | |
| 2984 | } | |
| 2983 | var new_file_data: [16]u8 = undefined; | |
| 2984 | try testing.expectEqualStrings("hello", try tmp.dir.readFile(new_path, &new_file_data)); | |
| 2985 | 2985 | } |
| 2986 | 2986 | |
| 2987 | 2987 | test "unlinkat" { |
| ... | ... | @@ -3179,12 +3179,8 @@ test "linkat" { |
| 3179 | 3179 | }, cqe); |
| 3180 | 3180 | |
| 3181 | 3181 | // Validate the second file |
| 3182 | const second_file = try tmp.dir.openFile(second_path, .{}); | |
| 3183 | defer second_file.close(); | |
| 3184 | ||
| 3185 | 3182 | var second_file_data: [16]u8 = undefined; |
| 3186 | const bytes_read = try second_file.readAll(&second_file_data); | |
| 3187 | try testing.expectEqualStrings("hello", second_file_data[0..bytes_read]); | |
| 3183 | try testing.expectEqualStrings("hello", try tmp.dir.readFile(second_path, &second_file_data)); | |
| 3188 | 3184 | } |
| 3189 | 3185 | |
| 3190 | 3186 | test "provide_buffers: read" { |
| ... | ... | @@ -3588,7 +3584,10 @@ const SocketTestHarness = struct { |
| 3588 | 3584 | |
| 3589 | 3585 | fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 3590 | 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 | 3591 | const listener_socket = try createListenerSocket(&address); |
| 3593 | 3592 | errdefer posix.close(listener_socket); |
| 3594 | 3593 | |
| ... | ... | @@ -3598,9 +3597,9 @@ fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 3598 | 3597 | _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0); |
| 3599 | 3598 | |
| 3600 | 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 | 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 | 3604 | try testing.expectEqual(@as(u32, 2), try ring.submit()); |
| 3606 | 3605 | |
| ... | ... | @@ -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 | 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 | 3641 | errdefer posix.close(listener_socket); |
| 3643 | 3642 | |
| 3644 | 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 | 3645 | try posix.listen(listener_socket, kernel_backlog); |
| 3647 | 3646 | |
| 3648 | 3647 | // set address to the OS-chosen IP/port. |
| 3649 | var slen: posix.socklen_t = address.getOsSockLen(); | |
| 3650 | try posix.getsockname(listener_socket, &address.any, &slen); | |
| 3648 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); | |
| 3649 | try posix.getsockname(listener_socket, addrAny(address), &slen); | |
| 3651 | 3650 | |
| 3652 | 3651 | return listener_socket; |
| 3653 | 3652 | } |
| ... | ... | @@ -3662,7 +3661,10 @@ test "accept multishot" { |
| 3662 | 3661 | }; |
| 3663 | 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 | 3668 | const listener_socket = try createListenerSocket(&address); |
| 3667 | 3669 | defer posix.close(listener_socket); |
| 3668 | 3670 | |
| ... | ... | @@ -3676,9 +3678,9 @@ test "accept multishot" { |
| 3676 | 3678 | var nr: usize = 4; // number of clients to connect |
| 3677 | 3679 | while (nr > 0) : (nr -= 1) { |
| 3678 | 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 | 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 | 3685 | // test accept completion |
| 3684 | 3686 | var cqe = try ring.copy_cqe(); |
| ... | ... | @@ -3756,7 +3758,10 @@ test "accept_direct" { |
| 3756 | 3758 | else => return err, |
| 3757 | 3759 | }; |
| 3758 | 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 | 3766 | // register direct file descriptors |
| 3762 | 3767 | var registered_fds = [_]posix.fd_t{-1} ** 2; |
| ... | ... | @@ -3779,8 +3784,8 @@ test "accept_direct" { |
| 3779 | 3784 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3780 | 3785 | |
| 3781 | 3786 | // connect |
| 3782 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3783 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3787 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3788 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | |
| 3784 | 3789 | defer posix.close(client); |
| 3785 | 3790 | |
| 3786 | 3791 | // accept completion |
| ... | ... | @@ -3813,8 +3818,8 @@ test "accept_direct" { |
| 3813 | 3818 | _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0); |
| 3814 | 3819 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 3815 | 3820 | // connect |
| 3816 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3817 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3821 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3822 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | |
| 3818 | 3823 | defer posix.close(client); |
| 3819 | 3824 | // completion with error |
| 3820 | 3825 | const cqe_accept = try ring.copy_cqe(); |
| ... | ... | @@ -3837,7 +3842,10 @@ test "accept_multishot_direct" { |
| 3837 | 3842 | }; |
| 3838 | 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 | 3850 | var registered_fds = [_]posix.fd_t{-1} ** 2; |
| 3843 | 3851 | try ring.register_files(registered_fds[0..]); |
| ... | ... | @@ -3855,8 +3863,8 @@ test "accept_multishot_direct" { |
| 3855 | 3863 | |
| 3856 | 3864 | for (registered_fds) |_| { |
| 3857 | 3865 | // connect |
| 3858 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3859 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3866 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3867 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | |
| 3860 | 3868 | defer posix.close(client); |
| 3861 | 3869 | |
| 3862 | 3870 | // accept completion |
| ... | ... | @@ -3870,8 +3878,8 @@ test "accept_multishot_direct" { |
| 3870 | 3878 | // Multishot is terminated (more flag is not set). |
| 3871 | 3879 | { |
| 3872 | 3880 | // connect |
| 3873 | const client = try posix.socket(address.any.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3874 | try posix.connect(client, &address.any, address.getOsSockLen()); | |
| 3881 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | |
| 3882 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | |
| 3875 | 3883 | defer posix.close(client); |
| 3876 | 3884 | // completion with error |
| 3877 | 3885 | const cqe_accept = try ring.copy_cqe(); |
| ... | ... | @@ -3944,7 +3952,10 @@ test "socket_direct/socket_direct_alloc/close_direct" { |
| 3944 | 3952 | try testing.expect(cqe_socket.res == 2); // returns registered file index |
| 3945 | 3953 | |
| 3946 | 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 | 3959 | const listener_socket = try createListenerSocket(&address); |
| 3949 | 3960 | defer posix.close(listener_socket); |
| 3950 | 3961 | const accept_userdata: u64 = 0xaaaaaaaa; |
| ... | ... | @@ -3954,7 +3965,7 @@ test "socket_direct/socket_direct_alloc/close_direct" { |
| 3954 | 3965 | // prepare accept |
| 3955 | 3966 | _ = try ring.accept(accept_userdata, listener_socket, null, null, 0); |
| 3956 | 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 | 3969 | connect_sqe.flags |= linux.IOSQE_FIXED_FILE; // fd is fixed file index |
| 3959 | 3970 | // submit both |
| 3960 | 3971 | try testing.expectEqual(@as(u32, 2), try ring.submit()); |
| ... | ... | @@ -4483,12 +4494,15 @@ test "bind/listen/connect" { |
| 4483 | 4494 | // LISTEN is higher required operation |
| 4484 | 4495 | if (!probe.is_supported(.LISTEN)) return error.SkipZigTest; |
| 4485 | 4496 | |
| 4486 | var addr = net.Address.initIp4([4]u8{ 127, 0, 0, 1 }, 0); | |
| 4487 | const proto: u32 = if (addr.any.family == linux.AF.UNIX) 0 else linux.IPPROTO.TCP; | |
| 4497 | var addr: linux.sockaddr.in = .{ | |
| 4498 | .port = 0, | |
| 4499 | .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), | |
| 4500 | }; | |
| 4501 | const proto: u32 = if (addr.family == linux.AF.UNIX) 0 else linux.IPPROTO.TCP; | |
| 4488 | 4502 | |
| 4489 | 4503 | const listen_fd = brk: { |
| 4490 | 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 | 4506 | try testing.expectEqual(1, try ring.submit()); |
| 4493 | 4507 | var cqe = try ring.copy_cqe(); |
| 4494 | 4508 | try testing.expectEqual(1, cqe.user_data); |
| ... | ... | @@ -4500,7 +4514,7 @@ test "bind/listen/connect" { |
| 4500 | 4514 | var optval: u32 = 1; |
| 4501 | 4515 | (try ring.setsockopt(2, listen_fd, linux.SOL.SOCKET, linux.SO.REUSEADDR, mem.asBytes(&optval))).link_next(); |
| 4502 | 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 | 4518 | _ = try ring.listen(5, listen_fd, 1, 0); |
| 4505 | 4519 | // Submit 4 operations |
| 4506 | 4520 | try testing.expectEqual(4, try ring.submit()); |
| ... | ... | @@ -4521,15 +4535,15 @@ test "bind/listen/connect" { |
| 4521 | 4535 | try testing.expectEqual(1, optval); |
| 4522 | 4536 | |
| 4523 | 4537 | // Read system assigned port into addr |
| 4524 | var addr_len: posix.socklen_t = addr.getOsSockLen(); | |
| 4525 | try posix.getsockname(listen_fd, &addr.any, &addr_len); | |
| 4538 | var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in); | |
| 4539 | try posix.getsockname(listen_fd, addrAny(&addr), &addr_len); | |
| 4526 | 4540 | |
| 4527 | 4541 | break :brk listen_fd; |
| 4528 | 4542 | }; |
| 4529 | 4543 | |
| 4530 | 4544 | const connect_fd = brk: { |
| 4531 | 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 | 4547 | try testing.expectEqual(1, try ring.submit()); |
| 4534 | 4548 | const cqe = try ring.copy_cqe(); |
| 4535 | 4549 | try testing.expectEqual(6, cqe.user_data); |
| ... | ... | @@ -4542,7 +4556,7 @@ test "bind/listen/connect" { |
| 4542 | 4556 | |
| 4543 | 4557 | // Prepare accept/connect operations |
| 4544 | 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 | 4560 | try testing.expectEqual(2, try ring.submit()); |
| 4547 | 4561 | // Get listener accepted socket |
| 4548 | 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 | 4618 | try testing.expectEqualSlices(u8, buffer_send, buffer_recv[0..buffer_send.len]); |
| 4605 | 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 | 3000 | windows.CloseHandle(sub_dir_handle); |
| 3001 | 3001 | } |
| 3002 | 3002 | |
| 3003 | pub const MakeDirError = error{ | |
| 3004 | /// In WASI, this error may occur when the file descriptor does | |
| 3005 | /// not hold the required rights to create a new directory relative to it. | |
| 3006 | AccessDenied, | |
| 3007 | PermissionDenied, | |
| 3008 | DiskQuota, | |
| 3009 | PathAlreadyExists, | |
| 3010 | SymLinkLoop, | |
| 3011 | LinkQuotaExceeded, | |
| 3012 | NameTooLong, | |
| 3013 | FileNotFound, | |
| 3014 | SystemResources, | |
| 3015 | NoSpaceLeft, | |
| 3016 | NotDir, | |
| 3017 | ReadOnlyFileSystem, | |
| 3018 | /// WASI-only; file paths must be valid UTF-8. | |
| 3019 | InvalidUtf8, | |
| 3020 | /// Windows-only; file paths provided by the user must be valid WTF-8. | |
| 3021 | /// https://wtf-8.codeberg.page/ | |
| 3022 | InvalidWtf8, | |
| 3023 | BadPathName, | |
| 3024 | NoDevice, | |
| 3025 | /// On Windows, `\\server` or `\\server\share` was not found. | |
| 3026 | NetworkNotFound, | |
| 3027 | } || UnexpectedError; | |
| 3003 | pub const MakeDirError = std.Io.Dir.MakeError; | |
| 3028 | 3004 | |
| 3029 | 3005 | /// Create a directory. |
| 3030 | 3006 | /// `mode` is ignored on Windows and WASI. |
lib/std/posix/test.zig+2-5| ... | ... | @@ -731,11 +731,8 @@ test "dup & dup2" { |
| 731 | 731 | try dup2ed.writeAll("dup2"); |
| 732 | 732 | } |
| 733 | 733 | |
| 734 | var file = try tmp.dir.openFile("os_dup_test", .{}); | |
| 735 | defer file.close(); | |
| 736 | ||
| 737 | var buf: [7]u8 = undefined; | |
| 738 | try testing.expectEqualStrings("dupdup2", buf[0..try file.readAll(&buf)]); | |
| 734 | var buffer: [8]u8 = undefined; | |
| 735 | try testing.expectEqualStrings("dupdup2", try tmp.dir.readFile("os_dup_test", &buffer)); | |
| 739 | 736 | } |
| 740 | 737 | |
| 741 | 738 | test "writev longer than IOV_MAX" { |
lib/std/process/Child.zig+26-11| ... | ... | @@ -1,5 +1,9 @@ |
| 1 | const std = @import("../std.zig"); | |
| 1 | const ChildProcess = @This(); | |
| 2 | ||
| 2 | 3 | const builtin = @import("builtin"); |
| 4 | const native_os = builtin.os.tag; | |
| 5 | ||
| 6 | const std = @import("../std.zig"); | |
| 3 | 7 | const unicode = std.unicode; |
| 4 | 8 | const fs = std.fs; |
| 5 | 9 | const process = std.process; |
| ... | ... | @@ -11,9 +15,7 @@ const mem = std.mem; |
| 11 | 15 | const EnvMap = std.process.EnvMap; |
| 12 | 16 | const maxInt = std.math.maxInt; |
| 13 | 17 | const assert = std.debug.assert; |
| 14 | const native_os = builtin.os.tag; | |
| 15 | 18 | const Allocator = std.mem.Allocator; |
| 16 | const ChildProcess = @This(); | |
| 17 | 19 | const ArrayList = std.ArrayList; |
| 18 | 20 | |
| 19 | 21 | pub const Id = switch (native_os) { |
| ... | ... | @@ -317,16 +319,23 @@ pub fn waitForSpawn(self: *ChildProcess) SpawnError!void { |
| 317 | 319 | |
| 318 | 320 | const err_pipe = self.err_pipe orelse return; |
| 319 | 321 | self.err_pipe = null; |
| 320 | ||
| 321 | 322 | // Wait for the child to report any errors in or before `execvpe`. |
| 322 | if (readIntFd(err_pipe)) |child_err_int| { | |
| 323 | posix.close(err_pipe); | |
| 323 | const report = readIntFd(err_pipe); | |
| 324 | posix.close(err_pipe); | |
| 325 | if (report) |child_err_int| { | |
| 324 | 326 | const child_err: SpawnError = @errorCast(@errorFromInt(child_err_int)); |
| 325 | 327 | self.term = child_err; |
| 326 | 328 | return child_err; |
| 327 | } else |_| { | |
| 328 | // Write end closed by CLOEXEC at the time of the `execvpe` call, indicating success! | |
| 329 | posix.close(err_pipe); | |
| 329 | } else |read_err| switch (read_err) { | |
| 330 | error.EndOfStream => { | |
| 331 | // Write end closed by CLOEXEC at the time of the `execvpe` call, | |
| 332 | // indicating success. | |
| 333 | }, | |
| 334 | else => { | |
| 335 | // Problem reading the error from the error reporting pipe. We | |
| 336 | // don't know if the child is alive or dead. Better to assume it is | |
| 337 | // alive so the resource does not risk being leaked. | |
| 338 | }, | |
| 330 | 339 | } |
| 331 | 340 | } |
| 332 | 341 | |
| ... | ... | @@ -1014,8 +1023,14 @@ fn writeIntFd(fd: i32, value: ErrInt) !void { |
| 1014 | 1023 | |
| 1015 | 1024 | fn readIntFd(fd: i32) !ErrInt { |
| 1016 | 1025 | var buffer: [8]u8 = undefined; |
| 1017 | var fr: std.fs.File.Reader = .initStreaming(.{ .handle = fd }, &buffer); | |
| 1018 | return @intCast(fr.interface.takeInt(u64, .little) catch return error.SystemResources); | |
| 1026 | var i: usize = 0; | |
| 1027 | while (i < buffer.len) { | |
| 1028 | const n = try std.posix.read(fd, buffer[i..]); | |
| 1029 | if (n == 0) return error.EndOfStream; | |
| 1030 | i += n; | |
| 1031 | } | |
| 1032 | const int = mem.readInt(u64, &buffer, .little); | |
| 1033 | return @intCast(int); | |
| 1019 | 1034 | } |
| 1020 | 1035 | |
| 1021 | 1036 | const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8); |
lib/std/tar/Writer.zig+20-18| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | const Writer = @This(); | |
| 2 | ||
| 1 | 3 | const std = @import("std"); |
| 4 | const Io = std.Io; | |
| 2 | 5 | const assert = std.debug.assert; |
| 3 | 6 | const testing = std.testing; |
| 4 | const Writer = @This(); | |
| 5 | 7 | |
| 6 | 8 | const block_size = @sizeOf(Header); |
| 7 | 9 | |
| ... | ... | @@ -14,7 +16,7 @@ pub const Options = struct { |
| 14 | 16 | mtime: u64 = 0, |
| 15 | 17 | }; |
| 16 | 18 | |
| 17 | underlying_writer: *std.Io.Writer, | |
| 19 | underlying_writer: *Io.Writer, | |
| 18 | 20 | prefix: []const u8 = "", |
| 19 | 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 | 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 | 43 | pub fn writeFile( |
| 42 | 44 | w: *Writer, |
| 43 | 45 | sub_path: []const u8, |
| 44 | file_reader: *std.fs.File.Reader, | |
| 46 | file_reader: *Io.File.Reader, | |
| 45 | 47 | stat_mtime: i128, |
| 46 | 48 | ) WriteFileError!void { |
| 47 | 49 | const size = try file_reader.getSize(); |
| ... | ... | @@ -58,7 +60,7 @@ pub fn writeFile( |
| 58 | 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 | 65 | /// Writes file reading file content from `reader`. Reads exactly `size` bytes |
| 64 | 66 | /// from `reader`, or returns `error.EndOfStream`. |
| ... | ... | @@ -66,7 +68,7 @@ pub fn writeFileStream( |
| 66 | 68 | w: *Writer, |
| 67 | 69 | sub_path: []const u8, |
| 68 | 70 | size: u64, |
| 69 | reader: *std.Io.Reader, | |
| 71 | reader: *Io.Reader, | |
| 70 | 72 | options: Options, |
| 71 | 73 | ) WriteFileStreamError!void { |
| 72 | 74 | try w.writeHeader(.regular, sub_path, "", size, options); |
| ... | ... | @@ -136,15 +138,15 @@ fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const [ |
| 136 | 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 | 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 | 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 | 150 | if (pos == 0) return; |
| 149 | 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 | 155 | /// "reasonable system must not assume that such a block exists when reading an |
| 154 | 156 | /// archive". Therefore, the Zig standard library recommends to not call this |
| 155 | 157 | /// function. |
| 156 | pub fn finishPedantically(w: *Writer) std.Io.Writer.Error!void { | |
| 158 | pub fn finishPedantically(w: *Writer) Io.Writer.Error!void { | |
| 157 | 159 | try w.underlying_writer.splatByteAll(0, block_size * 2); |
| 158 | 160 | } |
| 159 | 161 | |
| ... | ... | @@ -248,7 +250,7 @@ pub const Header = extern struct { |
| 248 | 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 | 254 | try h.updateChecksum(); |
| 253 | 255 | try bw.writeAll(std.mem.asBytes(h)); |
| 254 | 256 | } |
| ... | ... | @@ -396,14 +398,14 @@ test "write files" { |
| 396 | 398 | { |
| 397 | 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 | 402 | var w: Writer = .{ .underlying_writer = &output.writer }; |
| 401 | 403 | defer output.deinit(); |
| 402 | 404 | try w.setRoot(root); |
| 403 | 405 | for (files) |file| |
| 404 | 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 | 409 | var it: std.tar.Iterator = .init(&input, .{ |
| 408 | 410 | .file_name_buffer = &file_name_buffer, |
| 409 | 411 | .link_name_buffer = &link_name_buffer, |
| ... | ... | @@ -424,7 +426,7 @@ test "write files" { |
| 424 | 426 | try testing.expectEqual('/', actual.name[root.len..][0]); |
| 425 | 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 | 430 | defer content.deinit(); |
| 429 | 431 | try it.streamRemaining(actual, &content.writer); |
| 430 | 432 | try testing.expectEqualSlices(u8, expected.content, content.written()); |
| ... | ... | @@ -432,15 +434,15 @@ test "write files" { |
| 432 | 434 | } |
| 433 | 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 | 438 | var w: Writer = .{ .underlying_writer = &output.writer }; |
| 437 | 439 | defer output.deinit(); |
| 438 | 440 | for (files) |file| { |
| 439 | var content: std.Io.Reader = .fixed(file.content); | |
| 441 | var content: Io.Reader = .fixed(file.content); | |
| 440 | 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 | 446 | var it: std.tar.Iterator = .init(&input, .{ |
| 445 | 447 | .file_name_buffer = &file_name_buffer, |
| 446 | 448 | .link_name_buffer = &link_name_buffer, |
| ... | ... | @@ -452,7 +454,7 @@ test "write files" { |
| 452 | 454 | const expected = files[i]; |
| 453 | 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 | 458 | defer content.deinit(); |
| 457 | 459 | try it.streamRemaining(actual, &content.writer); |
| 458 | 460 | try testing.expectEqualSlices(u8, expected.content, content.written()); |
lib/std/zig.zig+1-1| ... | ... | @@ -559,7 +559,7 @@ test isUnderscore { |
| 559 | 559 | /// If the source can be UTF-16LE encoded, this function asserts that `gpa` |
| 560 | 560 | /// will align a byte-sized allocation to at least 2. Allocators that don't do |
| 561 | 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 | 563 | var buffer: std.ArrayList(u8) = .empty; |
| 564 | 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 | 442 | error.DeviceBusy, |
| 443 | 443 | error.InputOutput, |
| 444 | 444 | error.LockViolation, |
| 445 | error.FileSystem, | |
| 445 | 446 | |
| 446 | 447 | error.UnableToOpenElfFile, |
| 447 | 448 | error.UnhelpfulFile, |
| ... | ... | @@ -542,16 +543,15 @@ fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: T |
| 542 | 543 | return null; |
| 543 | 544 | } |
| 544 | 545 | |
| 545 | pub const AbiAndDynamicLinkerFromFileError = error{}; | |
| 546 | ||
| 547 | pub fn abiAndDynamicLinkerFromFile( | |
| 546 | fn abiAndDynamicLinkerFromFile( | |
| 548 | 547 | file_reader: *Io.File.Reader, |
| 549 | 548 | header: *const elf.Header, |
| 550 | 549 | cpu: Target.Cpu, |
| 551 | 550 | os: Target.Os, |
| 552 | 551 | ld_info_list: []const LdInfo, |
| 553 | 552 | query: Target.Query, |
| 554 | ) AbiAndDynamicLinkerFromFileError!Target { | |
| 553 | ) !Target { | |
| 554 | const io = file_reader.io; | |
| 555 | 555 | var result: Target = .{ |
| 556 | 556 | .cpu = cpu, |
| 557 | 557 | .os = os, |
| ... | ... | @@ -623,8 +623,8 @@ pub fn abiAndDynamicLinkerFromFile( |
| 623 | 623 | try file_reader.seekTo(shstr.sh_offset); |
| 624 | 624 | try file_reader.interface.readSliceAll(shstrtab); |
| 625 | 625 | const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: { |
| 626 | var it = header.iterateSectionHeaders(&file_reader.interface); | |
| 627 | while (it.next()) |shdr| { | |
| 626 | var it = header.iterateSectionHeaders(file_reader); | |
| 627 | while (try it.next()) |shdr| { | |
| 628 | 628 | const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue; |
| 629 | 629 | const sh_name = shstrtab[shdr.sh_name..end :0]; |
| 630 | 630 | if (mem.eql(u8, sh_name, ".dynstr")) break :find_dyn_str .{ |
| ... | ... | @@ -645,7 +645,7 @@ pub fn abiAndDynamicLinkerFromFile( |
| 645 | 645 | |
| 646 | 646 | var it = mem.tokenizeScalar(u8, rpath_list, ':'); |
| 647 | 647 | while (it.next()) |rpath| { |
| 648 | if (glibcVerFromRPath(rpath)) |ver| { | |
| 648 | if (glibcVerFromRPath(io, rpath)) |ver| { | |
| 649 | 649 | result.os.version_range.linux.glibc = ver; |
| 650 | 650 | return result; |
| 651 | 651 | } else |err| switch (err) { |
| ... | ... | @@ -660,7 +660,7 @@ pub fn abiAndDynamicLinkerFromFile( |
| 660 | 660 | // There is no DT_RUNPATH so we try to find libc.so.6 inside the same |
| 661 | 661 | // directory as the dynamic linker. |
| 662 | 662 | if (fs.path.dirname(dl_path)) |rpath| { |
| 663 | if (glibcVerFromRPath(rpath)) |ver| { | |
| 663 | if (glibcVerFromRPath(io, rpath)) |ver| { | |
| 664 | 664 | result.os.version_range.linux.glibc = ver; |
| 665 | 665 | return result; |
| 666 | 666 | } else |err| switch (err) { |
| ... | ... | @@ -725,7 +725,7 @@ pub fn abiAndDynamicLinkerFromFile( |
| 725 | 725 | @memcpy(path_buf[index..][0..abi.len], abi); |
| 726 | 726 | index += abi.len; |
| 727 | 727 | const rpath = path_buf[0..index]; |
| 728 | if (glibcVerFromRPath(rpath)) |ver| { | |
| 728 | if (glibcVerFromRPath(io, rpath)) |ver| { | |
| 729 | 729 | result.os.version_range.linux.glibc = ver; |
| 730 | 730 | return result; |
| 731 | 731 | } else |err| switch (err) { |
| ... | ... | @@ -842,18 +842,13 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion { |
| 842 | 842 | error.InvalidElfMagic, |
| 843 | 843 | error.InvalidElfEndian, |
| 844 | 844 | error.InvalidElfClass, |
| 845 | error.InvalidElfFile, | |
| 846 | 845 | error.InvalidElfVersion, |
| 847 | 846 | error.InvalidGnuLibCVersion, |
| 848 | 847 | error.EndOfStream, |
| 849 | 848 | => return error.GLibCNotFound, |
| 850 | 849 | |
| 851 | error.SystemResources, | |
| 852 | error.UnableToReadElfFile, | |
| 853 | error.Unexpected, | |
| 854 | error.FileSystem, | |
| 855 | error.ProcessNotFound, | |
| 856 | => |e| return e, | |
| 850 | error.ReadFailed => return file_reader.err.?, | |
| 851 | else => |e| return e, | |
| 857 | 852 | }; |
| 858 | 853 | } |
| 859 | 854 | |
| ... | ... | @@ -867,8 +862,8 @@ fn glibcVerFromSoFile(file_reader: *Io.File.Reader) !std.SemanticVersion { |
| 867 | 862 | try file_reader.seekTo(shstr.sh_offset); |
| 868 | 863 | try file_reader.interface.readSliceAll(shstrtab); |
| 869 | 864 | const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: { |
| 870 | var it = header.iterateSectionHeaders(&file_reader.interface); | |
| 871 | while (it.next()) |shdr| { | |
| 865 | var it = header.iterateSectionHeaders(file_reader); | |
| 866 | while (try it.next()) |shdr| { | |
| 872 | 867 | const end = mem.findScalarPos(u8, shstrtab, shdr.sh_name, 0) orelse continue; |
| 873 | 868 | const sh_name = shstrtab[shdr.sh_name..end :0]; |
| 874 | 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 | 877 | // strings that start with "GLIBC_2." indicate the existence of such a glibc version, |
| 883 | 878 | // and furthermore, that the system-installed glibc is at minimum that version. |
| 884 | 879 | var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 }; |
| 885 | ||
| 880 | var offset: u64 = 0; | |
| 886 | 881 | try file_reader.seekTo(dynstr.offset); |
| 887 | while (file_reader.interface.takeSentinel(0)) |s| { | |
| 888 | if (mem.startsWith(u8, s, "GLIBC_2.")) { | |
| 889 | const chopped = s["GLIBC_".len..]; | |
| 890 | const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) { | |
| 891 | error.Overflow => return error.InvalidGnuLibCVersion, | |
| 892 | error.InvalidVersion => return error.InvalidGnuLibCVersion, | |
| 893 | }; | |
| 894 | switch (ver.order(max_ver)) { | |
| 895 | .gt => max_ver = ver, | |
| 896 | .lt, .eq => continue, | |
| 882 | while (offset < dynstr.size) { | |
| 883 | if (file_reader.interface.takeSentinel(0)) |s| { | |
| 884 | if (mem.startsWith(u8, s, "GLIBC_2.")) { | |
| 885 | const chopped = s["GLIBC_".len..]; | |
| 886 | const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) { | |
| 887 | error.Overflow => return error.InvalidGnuLibCVersion, | |
| 888 | error.InvalidVersion => return error.InvalidGnuLibCVersion, | |
| 889 | }; | |
| 890 | switch (ver.order(max_ver)) { | |
| 891 | .gt => max_ver = ver, | |
| 892 | .lt, .eq => continue, | |
| 893 | } | |
| 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 | 1092 | error.ProcessFdQuotaExceeded, |
| 1092 | 1093 | error.SystemFdQuotaExceeded, |
| 1093 | 1094 | error.ProcessNotFound, |
| 1095 | error.Canceled, | |
| 1094 | 1096 | => |e| return e, |
| 1095 | 1097 | |
| 1096 | 1098 | error.ReadFailed => return file_reader.err.?, |
| 1097 | 1099 | |
| 1098 | error.UnableToReadElfFile, | |
| 1099 | error.InvalidElfClass, | |
| 1100 | error.InvalidElfVersion, | |
| 1101 | error.InvalidElfEndian, | |
| 1102 | error.InvalidElfFile, | |
| 1103 | error.InvalidElfMagic, | |
| 1104 | error.Unexpected, | |
| 1105 | error.EndOfStream, | |
| 1106 | error.NameTooLong, | |
| 1107 | error.StaticElfFile, | |
| 1108 | // Finally, we fall back on the standard path. | |
| 1109 | => |e| { | |
| 1100 | else => |e| { | |
| 1110 | 1101 | std.log.warn("encountered {t}; falling back to default ABI and dynamic linker", .{e}); |
| 1111 | 1102 | return defaultAbiAndDynamicLinker(cpu, os, query); |
| 1112 | 1103 | }, |
test/src/Cases.zig+2-11| ... | ... | @@ -455,8 +455,7 @@ pub fn lowerToBuildSteps( |
| 455 | 455 | parent_step: *std.Build.Step, |
| 456 | 456 | options: CaseTestOptions, |
| 457 | 457 | ) void { |
| 458 | const host = std.zig.system.resolveTargetQuery(.{}) catch |err| | |
| 459 | std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)}); | |
| 458 | const host = b.resolveTargetQuery(.{}); | |
| 460 | 459 | const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM"); |
| 461 | 460 | |
| 462 | 461 | for (self.cases.items) |case| { |
| ... | ... | @@ -587,7 +586,7 @@ pub fn lowerToBuildSteps( |
| 587 | 586 | }, |
| 588 | 587 | .Execution => |expected_stdout| no_exec: { |
| 589 | 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 | 590 | // We wouldn't be able to run the compiled C code. |
| 592 | 591 | break :no_exec; |
| 593 | 592 | } |
| ... | ... | @@ -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 | 974 | fn knownFileExtension(filename: []const u8) bool { |
| 984 | 975 | // List taken from `Compilation.classifyFileExt` in the compiler. |
| 985 | 976 | for ([_][]const u8{ |