Skip to content

Commit c872d08

Browse files
committed
probeFile: replace seek with fstatat
One less syscall, and no need to increase surface area of wasi.c Greetings from 11km above the ground, I really think we are living in the age of wonderful technology.
1 parent 6f1b0a0 commit c872d08

File tree

3 files changed

+60
-52
lines changed

3 files changed

+60
-52
lines changed

src/main.zig

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -670,18 +670,30 @@ fn probeFile(name: []const u8) !FileType {
670670
},
671671
};
672672

673-
var f = try fs.cwd().openFile(name, .{});
674-
std.os.lseek_SET(f.handle, 0) catch {
675-
return FileType{
676-
.stream = .{
677-
.handle = f.handle,
678-
.should_close_fd = true,
679-
},
680-
};
673+
// all files are files or directories in windwos. So if it's not stdin,
674+
// it's a file.
675+
if (builtin.os.tag == .windows)
676+
return FileType.regular;
677+
678+
// we are on POSIX or wasm and need to determine if a given path
679+
// is a stream. We have 2 choices here:
680+
// 1. open + lseek. If lseek fails, it's a stream. Always 2 syscalls.
681+
// 2. fstat + optional open. If fstat tells us it's a file, we return
682+
// 'regular'. If it's not a file, we open and return the file descriptor.
683+
// One syscall if the file is a regular file, 2 syscalls if it is a stream.
684+
// Use the latter.
685+
686+
var stat_info = try std.os.fstatat(fs.cwd().fd, name, 0);
687+
if (stat_info.mode & std.os.S.IFMT == std.os.S.IFREG)
688+
return FileType.regular;
689+
690+
// Not a regular file. Open it and return a file descriptor.
691+
return FileType{
692+
.stream = .{
693+
.handle = try std.os.open(name, std.os.O.RDONLY, 0),
694+
.should_close_fd = true,
695+
},
681696
};
682-
683-
f.close();
684-
return FileType.regular;
685697
}
686698

687699
// Read an open file descriptor to a new file in `dst_dir`. Return a file name

stage1/wasi.c

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -727,20 +727,6 @@ uint32_t wasi_snapshot_preview1_fd_readdir(uint32_t fd, uint32_t buf, uint32_t b
727727
return wasi_errno_success;
728728
}
729729

730-
uint32_t wasi_snapshot_preview1_fd_seek(uint32_t fd, uint64_t size, char whence, uint32_t res_size) {
731-
(void)fd;
732-
(void)size;
733-
(void)whence;
734-
(void)res_size;
735-
#if LOG_TRACE
736-
fprintf(stderr, "wasi_snapshot_preview1_fd_seek(%u, %llu, %d, %d\n", fd, (unsigned long long)size, whence, res_size);
737-
#endif
738-
739-
panic("unimplemented");
740-
return wasi_errno_success;
741-
}
742-
743-
744730
uint32_t wasi_snapshot_preview1_fd_write(uint32_t fd, uint32_t iovs, uint32_t iovs_len, uint32_t res_size) {
745731
uint8_t *const m = *wasm_memory;
746732
struct wasi_ciovec *iovs_ptr = (struct wasi_ciovec *)&m[iovs];

test/cli.zig

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,8 @@ pub fn main() !void {
4343
.{ .func = testGodboltApi, .name = "godbolt API" },
4444
.{ .func = testMissingOutputPath, .name = "missing output path" },
4545
.{ .func = testZigFmt, .name = "zig fmt" },
46-
}
47-
// Test writer was not able to figure out how to start a child process and
48-
// give an fd to it's stdin. fork/exec works, but we are limiting ourselves
49-
// to POSIX.
50-
++ if (builtin.os.tag == .windows) [0]Test{} else [1]Test{.{ .func = testZigCC, .name = "zig cc" }};
46+
.{ .func = testZigCC, .name = "zig cc" },
47+
};
5148
inline for (tests) |t| {
5249
try fs.cwd().deleteTree(dir_path);
5350
try fs.cwd().makeDir(dir_path);
@@ -68,7 +65,7 @@ fn printCmd(cwd: []const u8, argv: []const []const u8) void {
6865
std.debug.print("\n", .{});
6966
}
7067

71-
fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult {
68+
fn exec(cwd: []const u8, expect_code: u8, argv: []const []const u8) !ChildProcess.ExecResult {
7269
const max_output_size = 100 * 1024;
7370
const result = ChildProcess.exec(.{
7471
.allocator = a,
@@ -82,8 +79,11 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess
8279
};
8380
switch (result.term) {
8481
.Exited => |code| {
85-
if ((code != 0) == expect_0) {
86-
std.debug.print("The following command exited with error code {}:\n", .{code});
82+
if (code != expect_code) {
83+
std.debug.print(
84+
"The following command exited with error code {}, expected {}:\n",
85+
.{ code, expect_code },
86+
);
8787
printCmd(cwd, argv);
8888
std.debug.print("stderr:\n{s}\n", .{result.stderr});
8989
return error.CommandFailed;
@@ -100,34 +100,45 @@ fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess
100100
}
101101

102102
fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void {
103-
_ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" });
104-
const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" });
103+
_ = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "init-lib" });
104+
const test_result = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "build", "test" });
105105
try testing.expectStringEndsWith(test_result.stderr, "All 1 tests passed.\n");
106106
}
107107

108108
fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
109-
_ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
110-
const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" });
109+
_ = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "init-exe" });
110+
const run_result = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "build", "run" });
111111
try testing.expectEqualStrings("All your codebase are belong to us.\n", run_result.stderr);
112112
try testing.expectEqualStrings("Run `zig build test` to run the tests.\n", run_result.stdout);
113113
}
114114

115115
fn testZigCC(zig_exe: []const u8, dir_path: []const u8) !void {
116+
// Test writer was not able to figure out how to start a child process and
117+
// give an fd to it's stdin. fork/exec works, but we are limiting ourselves
118+
// to POSIX.
119+
if (builtin.os.tag != .windows)
120+
// no error.SkipZigTest, because this is not a "test {...}" block.
121+
return;
122+
123+
return testZigCCPosix(zig_exe, dir_path);
124+
}
125+
126+
fn testZigCCPosix(zig_exe: []const u8, dir_path: []const u8) !void {
116127
var tmp = testing.tmpDir(.{});
117128
defer tmp.cleanup();
118-
try tmp.dir.writeFile("truth.c", "int truth = 42;");
129+
try tmp.dir.writeFile("truth.c", "int main() { return 42; }");
119130
var infile = try tmp.dir.openFile("truth.c", .{});
120131

121132
const pid_result = try std.os.fork();
122133
const zig_exe0 = try a.dupeZ(u8, zig_exe);
123-
const outfile = try fs.path.join(a, &[_][]const u8{ dir_path, "truth.o" });
134+
const outfile = try fs.path.joinZ(a, &[_][]const u8{ dir_path, "truth" });
124135
if (pid_result == 0) {
125136
try std.os.dup2(infile.handle, std.os.STDIN_FILENO);
126137
const argv = &[_:null]?[*:0]const u8{
127138
zig_exe0, "cc",
128-
"-o", try a.dupeZ(u8, outfile),
139+
"-o", outfile,
129140
"-x", "c",
130-
"-c", "-",
141+
"-",
131142
};
132143
const envp = &[_:null]?[*:0]const u8{
133144
try std.fmt.allocPrintZ(a, "ZIG_GLOBAL_CACHE_DIR={s}", .{dir_path}),
@@ -144,9 +155,8 @@ fn testZigCC(zig_exe: []const u8, dir_path: []const u8) !void {
144155

145156
try testing.expectEqual(@as(u32, 0), res.status);
146157

147-
// absence of failure to open the file means success
148-
const outf = try fs.cwd().openFile(outfile, .{});
149-
outf.close();
158+
// run the compiled executable and check if it's telling the truth.
159+
_ = try exec(dir_path, 42, &[_][]const u8{outfile});
150160
}
151161

152162
fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
@@ -181,7 +191,7 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
181191
const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path});
182192
try args.append(emit_asm_arg);
183193

184-
_ = try exec(dir_path, true, args.items);
194+
_ = try exec(dir_path, 0, args.items);
185195

186196
const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize));
187197
try testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null);
@@ -190,38 +200,38 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
190200
}
191201

192202
fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
193-
_ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
203+
_ = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "init-exe" });
194204
const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" });
195205
const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path});
196206
const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" });
197-
const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg });
207+
const result = try exec(dir_path, 1, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg });
198208
const s = std.fs.path.sep_str;
199209
const expected: []const u8 = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n";
200210
try testing.expectEqualStrings(expected, result.stderr);
201211
}
202212

203213
fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
204-
_ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" });
214+
_ = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "init-exe" });
205215

206216
const unformatted_code = " // no reason for indent";
207217

208218
const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
209219
try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
210220

211-
const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
221+
const run_result1 = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
212222
// stderr should be file path + \n
213223
try testing.expect(std.mem.startsWith(u8, run_result1.stdout, fmt1_zig_path));
214224
try testing.expect(run_result1.stdout.len == fmt1_zig_path.len + 1 and run_result1.stdout[run_result1.stdout.len - 1] == '\n');
215225

216226
const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
217227
try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
218228

219-
const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
229+
const run_result2 = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "fmt", dir_path });
220230
// running it on the dir, only the new file should be changed
221231
try testing.expect(std.mem.startsWith(u8, run_result2.stdout, fmt2_zig_path));
222232
try testing.expect(run_result2.stdout.len == fmt2_zig_path.len + 1 and run_result2.stdout[run_result2.stdout.len - 1] == '\n');
223233

224-
const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
234+
const run_result3 = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "fmt", dir_path });
225235
// both files have been formatted, nothing should change now
226236
try testing.expect(run_result3.stdout.len == 0);
227237

@@ -230,7 +240,7 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
230240
var unformatted_code_utf16 = "\xff\xfe \x00 \x00 \x00 \x00/\x00/\x00 \x00n\x00o\x00 \x00r\x00e\x00a\x00s\x00o\x00n\x00";
231241
try fs.cwd().writeFile(fmt4_zig_path, unformatted_code_utf16);
232242

233-
const run_result4 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path });
243+
const run_result4 = try exec(dir_path, 0, &[_][]const u8{ zig_exe, "fmt", dir_path });
234244
try testing.expect(std.mem.startsWith(u8, run_result4.stdout, fmt4_zig_path));
235245
try testing.expect(run_result4.stdout.len == fmt4_zig_path.len + 1 and run_result4.stdout[run_result4.stdout.len - 1] == '\n');
236246
}

0 commit comments

Comments
 (0)