authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-31 04:47:55-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-10-31 04:47:55-04:00
log9e234d4208725b818812765740b474f049d69788
treecfb7b0602d8d5c4219797ef95cc763a970e53586
parent7a96aca39e8eabccf69f5c65cd60a96aa4a7666c

breaking change to std.io API

* Merge io.InStream and io.OutStream into io.File * Introduce io.OutStream and io.InStream interfaces - io.File implements both of these * Move mem.IncrementingAllocator to heap.IncrementingAllocator Instead of: ``` %return std.io.stderr.printf("hello\n"); ``` now do: ``` std.debug.warn("hello\n"); ``` To print to stdout, see `io.getStdOut()`. * Rename std.ArrayList.resizeDown to std.ArrayList.shrink.

21 files changed, 958 insertions(+), 1020 deletions(-)

CMakeLists.txt+2-1
......@@ -521,6 +521,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/fmt/errol/index.zig" DESTINATION "${ZIG_S
521521install(FILES "${CMAKE_SOURCE_DIR}/std/fmt/errol/lookup.zig" DESTINATION "${ZIG_STD_DEST}/fmt/errol")
522522install(FILES "${CMAKE_SOURCE_DIR}/std/fmt/index.zig" DESTINATION "${ZIG_STD_DEST}/fmt")
523523install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}")
524install(FILES "${CMAKE_SOURCE_DIR}/std/heap.zig" DESTINATION "${ZIG_STD_DEST}")
524525install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")
525526install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")
526527install(FILES "${CMAKE_SOURCE_DIR}/std/linked_list.zig" DESTINATION "${ZIG_STD_DEST}")
......@@ -605,10 +606,10 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/fixunstfdi.zig" DESTI
605606install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/fixunstfsi.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
606607install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/fixunstfti.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
607608install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/index.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
608install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivti3.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
609609install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivmod.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
610610install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivmoddi4.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
611611install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivmodti4.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
612install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivti3.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
612613install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/umodti3.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
613614install(FILES "${CMAKE_SOURCE_DIR}/std/special/panic.zig" DESTINATION "${ZIG_STD_DEST}/special")
614615install(FILES "${CMAKE_SOURCE_DIR}/std/special/test_runner.zig" DESTINATION "${ZIG_STD_DEST}/special")
example/cat/main.zig+18-13
......@@ -2,47 +2,52 @@ const std = @import("std");
22const io = std.io;
33const mem = std.mem;
44const os = std.os;
5const warn = std.debug.warn;
56
67pub fn main() -> %void {
78 const allocator = &std.debug.global_allocator;
89 var args_it = os.args();
910 const exe = %return unwrapArg(??args_it.next(allocator));
1011 var catted_anything = false;
12 var stdout_file = %return io.getStdOut();
13 const stdout = &stdout_file.out_stream;
14
1115 while (args_it.next(allocator)) |arg_or_err| {
1216 const arg = %return unwrapArg(arg_or_err);
1317 if (mem.eql(u8, arg, "-")) {
1418 catted_anything = true;
15 %return cat_stream(&io.stdin);
19 var stdin_file = %return io.getStdIn();
20 %return cat_stream(stdout, &stdin_file.in_stream);
1621 } else if (arg[0] == '-') {
1722 return usage(exe);
1823 } else {
19 var is = io.InStream.open(arg, null) %% |err| {
20 %%io.stderr.printf("Unable to open file: {}\n", @errorName(err));
24 var file = io.File.openRead(arg, null) %% |err| {
25 warn("Unable to open file: {}\n", @errorName(err));
2126 return err;
2227 };
23 defer is.close();
28 defer file.close();
2429
2530 catted_anything = true;
26 %return cat_stream(&is);
31 %return cat_stream(stdout, &file.in_stream);
2732 }
2833 }
2934 if (!catted_anything) {
30 %return cat_stream(&io.stdin);
35 var stdin_file = %return io.getStdIn();
36 %return cat_stream(stdout, &stdin_file.in_stream);
3137 }
32 %return io.stdout.flush();
3338}
3439
3540fn usage(exe: []const u8) -> %void {
36 %%io.stderr.printf("Usage: {} [FILE]...\n", exe);
41 warn("Usage: {} [FILE]...\n", exe);
3742 return error.Invalid;
3843}
3944
40fn cat_stream(is: &io.InStream) -> %void {
45fn cat_stream(stdout: &io.OutStream, is: &io.InStream) -> %void {
4146 var buf: [1024 * 4]u8 = undefined;
4247
4348 while (true) {
4449 const bytes_read = is.read(buf[0..]) %% |err| {
45 %%io.stderr.printf("Unable to read from stream: {}\n", @errorName(err));
50 warn("Unable to read from stream: {}\n", @errorName(err));
4651 return err;
4752 };
4853
......@@ -50,8 +55,8 @@ fn cat_stream(is: &io.InStream) -> %void {
5055 break;
5156 }
5257
53 io.stdout.write(buf[0..bytes_read]) %% |err| {
54 %%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err));
58 stdout.write(buf[0..bytes_read]) %% |err| {
59 warn("Unable to write to stdout: {}\n", @errorName(err));
5560 return err;
5661 };
5762 }
......@@ -59,7 +64,7 @@ fn cat_stream(is: &io.InStream) -> %void {
5964
6065fn unwrapArg(arg: %[]u8) -> %[]u8 {
6166 return arg %% |err| {
62 %%io.stderr.printf("Unable to parse command line: {}\n", err);
67 warn("Unable to parse command line: {}\n", err);
6368 return err;
6469 };
6570}
example/guess_number/main.zig+14-8
......@@ -5,7 +5,13 @@ const Rand = std.rand.Rand;
55const os = std.os;
66
77pub fn main() -> %void {
8 %%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n");
8 var stdout_file = %return io.getStdOut();
9 const stdout = &stdout_file.out_stream;
10
11 var stdin_file = %return io.getStdIn();
12 const stdin = &stdin_file.in_stream;
13
14 %return stdout.print("Welcome to the Guess Number Game in Zig.\n");
915
1016 var seed_bytes: [@sizeOf(usize)]u8 = undefined;
1117 %%os.getRandomBytes(seed_bytes[0..]);
......@@ -15,24 +21,24 @@ pub fn main() -> %void {
1521 const answer = rand.range(u8, 0, 100) + 1;
1622
1723 while (true) {
18 %%io.stdout.printf("\nGuess a number between 1 and 100: ");
24 %return stdout.print("\nGuess a number between 1 and 100: ");
1925 var line_buf : [20]u8 = undefined;
2026
21 const line_len = io.stdin.read(line_buf[0..]) %% |err| {
22 %%io.stdout.printf("Unable to read from stdin: {}\n", @errorName(err));
27 const line_len = stdin.read(line_buf[0..]) %% |err| {
28 %return stdout.print("Unable to read from stdin: {}\n", @errorName(err));
2329 return err;
2430 };
2531
2632 const guess = fmt.parseUnsigned(u8, line_buf[0..line_len - 1], 10) %% {
27 %%io.stdout.printf("Invalid number.\n");
33 %return stdout.print("Invalid number.\n");
2834 continue;
2935 };
3036 if (guess > answer) {
31 %%io.stdout.printf("Guess lower.\n");
37 %return stdout.print("Guess lower.\n");
3238 } else if (guess < answer) {
33 %%io.stdout.printf("Guess higher.\n");
39 %return stdout.print("Guess higher.\n");
3440 } else {
35 %%io.stdout.printf("You win!\n");
41 %return stdout.print("You win!\n");
3642 return;
3743 }
3844 }
example/hello_world/hello.zig+7-2
......@@ -1,5 +1,10 @@
1const io = @import("std").io;
1const std = @import("std");
22
33pub fn main() -> %void {
4 %return io.stdout.printf("Hello, world!\n");
4 // If this program is run without stdout attached, exit with an error.
5 var stdout_file = %return std.io.getStdOut();
6 const stdout = &stdout_file.out_stream;
7 // If this program encounters pipe failure when printing to stdout, exit
8 // with an error.
9 %return stdout.print("Hello, world!\n");
510}
std/array_list.zig+1-1
......@@ -70,7 +70,7 @@ pub fn ArrayList(comptime T: type) -> type{
7070 l.len = new_len;
7171 }
7272
73 pub fn resizeDown(l: &Self, new_len: usize) {
73 pub fn shrink(l: &Self, new_len: usize) {
7474 assert(new_len <= l.len);
7575 l.len = new_len;
7676 }
std/buffer.zig+6
......@@ -71,6 +71,12 @@ pub const Buffer = struct {
7171 return self.list.toSliceConst()[0..self.len()];
7272 }
7373
74 pub fn shrink(self: &Buffer, new_len: usize) {
75 assert(new_len <= self.len());
76 self.list.shrink(new_len + 1);
77 self.list.items[self.len()] = 0;
78 }
79
7480 pub fn resize(self: &Buffer, new_len: usize) -> %void {
7581 %return self.list.resize(new_len + 1);
7682 self.list.items[self.len()] = 0;
std/build.zig+46-45
......@@ -1,17 +1,19 @@
1const std = @import("index.zig");
12const builtin = @import("builtin");
2const io = @import("io.zig");
3const mem = @import("mem.zig");
4const debug = @import("debug.zig");
3const io = std.io;
4const mem = std.mem;
5const debug = std.debug;
56const assert = debug.assert;
6const ArrayList = @import("array_list.zig").ArrayList;
7const HashMap = @import("hash_map.zig").HashMap;
8const Allocator = @import("mem.zig").Allocator;
9const os = @import("os/index.zig");
7const warn = std.debug.warn;
8const ArrayList = std.ArrayList;
9const HashMap = std.HashMap;
10const Allocator = mem.Allocator;
11const os = std.os;
1012const StdIo = os.ChildProcess.StdIo;
1113const Term = os.ChildProcess.Term;
12const BufSet = @import("buf_set.zig").BufSet;
13const BufMap = @import("buf_map.zig").BufMap;
14const fmt_lib = @import("fmt/index.zig");
14const BufSet = std.BufSet;
15const BufMap = std.BufMap;
16const fmt_lib = std.fmt;
1517
1618error ExtraArg;
1719error UncleanExit;
......@@ -280,7 +282,7 @@ pub const Builder = struct {
280282
281283 for (self.installed_files.toSliceConst()) |installed_file| {
282284 if (self.verbose) {
283 %%io.stderr.printf("rm {}\n", installed_file);
285 warn("rm {}\n", installed_file);
284286 }
285287 _ = os.deleteFile(self.allocator, installed_file);
286288 }
......@@ -290,7 +292,7 @@ pub const Builder = struct {
290292
291293 fn makeOneStep(self: &Builder, s: &Step) -> %void {
292294 if (s.loop_flag) {
293 %%io.stderr.printf("Dependency loop detected:\n {}\n", s.name);
295 warn("Dependency loop detected:\n {}\n", s.name);
294296 return error.DependencyLoopDetected;
295297 }
296298 s.loop_flag = true;
......@@ -298,7 +300,7 @@ pub const Builder = struct {
298300 for (s.dependencies.toSlice()) |dep| {
299301 self.makeOneStep(dep) %% |err| {
300302 if (err == error.DependencyLoopDetected) {
301 %%io.stderr.printf(" {}\n", s.name);
303 warn(" {}\n", s.name);
302304 }
303305 return err;
304306 };
......@@ -315,7 +317,7 @@ pub const Builder = struct {
315317 return &top_level_step.step;
316318 }
317319 }
318 %%io.stderr.printf("Cannot run step '{}' because it does not exist\n", name);
320 warn("Cannot run step '{}' because it does not exist\n", name);
319321 return error.InvalidStepName;
320322 }
321323
......@@ -326,12 +328,12 @@ pub const Builder = struct {
326328 const word = it.next() ?? break;
327329 if (mem.eql(u8, word, "-isystem")) {
328330 const include_path = it.next() ?? {
329 %%io.stderr.printf("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");
331 warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");
330332 break;
331333 };
332334 self.addCIncludePath(include_path);
333335 } else {
334 %%io.stderr.printf("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}\n", word);
336 warn("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}\n", word);
335337 break;
336338 }
337339 }
......@@ -344,7 +346,7 @@ pub const Builder = struct {
344346 const word = it.next() ?? break;
345347 if (mem.eql(u8, word, "-rpath")) {
346348 const rpath = it.next() ?? {
347 %%io.stderr.printf("Expected argument after -rpath in NIX_LDFLAGS\n");
349 warn("Expected argument after -rpath in NIX_LDFLAGS\n");
348350 break;
349351 };
350352 self.addRPath(rpath);
......@@ -352,7 +354,7 @@ pub const Builder = struct {
352354 const lib_path = word[2..];
353355 self.addLibPath(lib_path);
354356 } else {
355 %%io.stderr.printf("Unrecognized C flag from NIX_LDFLAGS: {}\n", word);
357 warn("Unrecognized C flag from NIX_LDFLAGS: {}\n", word);
356358 break;
357359 }
358360 }
......@@ -384,13 +386,13 @@ pub const Builder = struct {
384386 } else if (mem.eql(u8, s, "false")) {
385387 return false;
386388 } else {
387 %%io.stderr.printf("Expected -D{} to be a boolean, but received '{}'\n", name, s);
389 warn("Expected -D{} to be a boolean, but received '{}'\n", name, s);
388390 self.markInvalidUserInput();
389391 return null;
390392 }
391393 },
392394 UserValue.List => {
393 %%io.stderr.printf("Expected -D{} to be a boolean, but received a list.\n", name);
395 warn("Expected -D{} to be a boolean, but received a list.\n", name);
394396 self.markInvalidUserInput();
395397 return null;
396398 },
......@@ -399,12 +401,12 @@ pub const Builder = struct {
399401 TypeId.Float => debug.panic("TODO float options to build script"),
400402 TypeId.String => switch (entry.value.value) {
401403 UserValue.Flag => {
402 %%io.stderr.printf("Expected -D{} to be a string, but received a boolean.\n", name);
404 warn("Expected -D{} to be a string, but received a boolean.\n", name);
403405 self.markInvalidUserInput();
404406 return null;
405407 },
406408 UserValue.List => {
407 %%io.stderr.printf("Expected -D{} to be a string, but received a list.\n", name);
409 warn("Expected -D{} to be a string, but received a list.\n", name);
408410 self.markInvalidUserInput();
409411 return null;
410412 },
......@@ -437,7 +439,7 @@ pub const Builder = struct {
437439 } else if (!release_fast and !release_safe) {
438440 builtin.Mode.Debug
439441 } else {
440 %%io.stderr.printf("Both -Drelease-safe and -Drelease-fast specified");
442 warn("Both -Drelease-safe and -Drelease-fast specified");
441443 self.markInvalidUserInput();
442444 builtin.Mode.Debug
443445 };
......@@ -474,7 +476,7 @@ pub const Builder = struct {
474476 });
475477 },
476478 UserValue.Flag => {
477 %%io.stderr.printf("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
479 warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
478480 return true;
479481 },
480482 }
......@@ -490,11 +492,11 @@ pub const Builder = struct {
490492 })) |*prev_value| {
491493 switch (prev_value.value) {
492494 UserValue.Scalar => |s| {
493 %%io.stderr.printf("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
495 warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
494496 return true;
495497 },
496498 UserValue.List => {
497 %%io.stderr.printf("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
499 warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
498500 return true;
499501 },
500502 UserValue.Flag => {},
......@@ -536,7 +538,7 @@ pub const Builder = struct {
536538 while (true) {
537539 const entry = it.next() ?? break;
538540 if (!entry.value.used) {
539 %%io.stderr.printf("Invalid option: -D{}\n\n", entry.key);
541 warn("Invalid option: -D{}\n\n", entry.key);
540542 self.markInvalidUserInput();
541543 }
542544 }
......@@ -549,11 +551,11 @@ pub const Builder = struct {
549551 }
550552
551553 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) {
552 if (cwd) |yes_cwd| %%io.stderr.print("cd {} && ", yes_cwd);
554 if (cwd) |yes_cwd| warn("cd {} && ", yes_cwd);
553555 for (argv) |arg| {
554 %%io.stderr.print("{} ", arg);
556 warn("{} ", arg);
555557 }
556 %%io.stderr.printf("\n");
558 warn("\n");
557559 }
558560
559561 fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
......@@ -570,20 +572,20 @@ pub const Builder = struct {
570572 child.env_map = env_map;
571573
572574 const term = child.spawnAndWait() %% |err| {
573 %%io.stderr.printf("Unable to spawn {}: {}\n", argv[0], @errorName(err));
575 warn("Unable to spawn {}: {}\n", argv[0], @errorName(err));
574576 return err;
575577 };
576578
577579 switch (term) {
578580 Term.Exited => |code| {
579581 if (code != 0) {
580 %%io.stderr.printf("The following command exited with error code {}:\n", code);
582 warn("The following command exited with error code {}:\n", code);
581583 printCmd(cwd, argv);
582584 return error.UncleanExit;
583585 }
584586 },
585587 else => {
586 %%io.stderr.printf("The following command terminated unexpectedly:\n");
588 warn("The following command terminated unexpectedly:\n");
587589 printCmd(cwd, argv);
588590
589591 return error.UncleanExit;
......@@ -594,7 +596,7 @@ pub const Builder = struct {
594596
595597 pub fn makePath(self: &Builder, path: []const u8) -> %void {
596598 os.makePath(self.allocator, self.pathFromRoot(path)) %% |err| {
597 %%io.stderr.printf("Unable to create path {}: {}\n", path, @errorName(err));
599 warn("Unable to create path {}: {}\n", path, @errorName(err));
598600 return err;
599601 };
600602 }
......@@ -633,17 +635,17 @@ pub const Builder = struct {
633635
634636 fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
635637 if (self.verbose) {
636 %%io.stderr.printf("cp {} {}\n", source_path, dest_path);
638 warn("cp {} {}\n", source_path, dest_path);
637639 }
638640
639641 const dirname = os.path.dirname(dest_path);
640642 const abs_source_path = self.pathFromRoot(source_path);
641643 os.makePath(self.allocator, dirname) %% |err| {
642 %%io.stderr.printf("Unable to create path {}: {}\n", dirname, @errorName(err));
644 warn("Unable to create path {}: {}\n", dirname, @errorName(err));
643645 return err;
644646 };
645647 os.copyFileMode(self.allocator, abs_source_path, dest_path, mode) %% |err| {
646 %%io.stderr.printf("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err));
648 warn("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err));
647649 return err;
648650 };
649651 }
......@@ -1103,7 +1105,7 @@ pub const LibExeObjStep = struct {
11031105 assert(self.is_zig);
11041106
11051107 if (self.root_src == null and self.object_files.len == 0 and self.assembly_files.len == 0) {
1106 %%io.stderr.printf("{}: linker needs 1 or more objects to link\n", self.step.name);
1108 warn("{}: linker needs 1 or more objects to link\n", self.step.name);
11071109 return error.NeedAnObject;
11081110 }
11091111
......@@ -1799,11 +1801,11 @@ pub const WriteFileStep = struct {
17991801 const full_path = self.builder.pathFromRoot(self.file_path);
18001802 const full_path_dir = os.path.dirname(full_path);
18011803 os.makePath(self.builder.allocator, full_path_dir) %% |err| {
1802 %%io.stderr.printf("unable to make path {}: {}\n", full_path_dir, @errorName(err));
1804 warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
18031805 return err;
18041806 };
18051807 io.writeFile(full_path, self.data, self.builder.allocator) %% |err| {
1806 %%io.stderr.printf("unable to write {}: {}\n", full_path, @errorName(err));
1808 warn("unable to write {}: {}\n", full_path, @errorName(err));
18071809 return err;
18081810 };
18091811 }
......@@ -1824,8 +1826,7 @@ pub const LogStep = struct {
18241826
18251827 fn make(step: &Step) -> %void {
18261828 const self = @fieldParentPtr(LogStep, "step", step);
1827 %%io.stderr.write(self.data);
1828 %%io.stderr.flush();
1829 warn("{}", self.data);
18291830 }
18301831};
18311832
......@@ -1847,7 +1848,7 @@ pub const RemoveDirStep = struct {
18471848
18481849 const full_path = self.builder.pathFromRoot(self.dir_path);
18491850 os.deleteTree(self.builder.allocator, full_path) %% |err| {
1850 %%io.stderr.printf("Unable to remove {}: {}\n", full_path, @errorName(err));
1851 warn("Unable to remove {}: {}\n", full_path, @errorName(err));
18511852 return err;
18521853 };
18531854 }
......@@ -1896,13 +1897,13 @@ fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_maj
18961897 // sym link for libfoo.so.1 to libfoo.so.1.2.3
18971898 const major_only_path = %%os.path.join(allocator, out_dir, filename_major_only);
18981899 os.atomicSymLink(allocator, out_basename, major_only_path) %% |err| {
1899 %%io.stderr.printf("Unable to symlink {} -> {}\n", major_only_path, out_basename);
1900 warn("Unable to symlink {} -> {}\n", major_only_path, out_basename);
19001901 return err;
19011902 };
19021903 // sym link for libfoo.so to libfoo.so.1
19031904 const name_only_path = %%os.path.join(allocator, out_dir, filename_name_only);
19041905 os.atomicSymLink(allocator, filename_major_only, name_only_path) %% |err| {
1905 %%io.stderr.printf("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
1906 warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
19061907 return err;
19071908 };
19081909}
std/debug.zig+101-98
......@@ -1,10 +1,11 @@
1const math = @import("math/index.zig");
2const mem = @import("mem.zig");
3const io = @import("io.zig");
4const os = @import("os/index.zig");
1const std = @import("index.zig");
2const math = std.math;
3const mem = std.mem;
4const io = std.io;
5const os = std.os;
56const elf = @import("elf.zig");
67const DW = @import("dwarf.zig");
7const ArrayList = @import("array_list.zig").ArrayList;
8const ArrayList = std.ArrayList;
89const builtin = @import("builtin");
910
1011error MissingDebugInfo;
......@@ -12,10 +13,42 @@ error InvalidDebugInfo;
1213error UnsupportedDebugInfo;
1314
1415
16/// Tries to write to stderr, unbuffered, and ignores any error returned.
17/// Does not append a newline.
18/// TODO atomic/multithread support
19var stderr_file: io.File = undefined;
20var stderr_stream: ?&io.OutStream = null;
21pub fn warn(comptime fmt: []const u8, args: ...) {
22 const stderr = getStderrStream() %% return;
23 stderr.print(fmt, args) %% return;
24}
25fn getStderrStream() -> %&io.OutStream {
26 if (stderr_stream) |st| {
27 return st;
28 } else {
29 stderr_file = %return io.getStdErr();
30 const st = &stderr_file.out_stream;
31 stderr_stream = st;
32 return st;
33 };
34}
35
36/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
37pub fn dumpStackTrace() {
38 const stderr = getStderrStream() %% return;
39 writeStackTrace(stderr, &global_allocator, stderr_file.isTty(), 1) %% return;
40}
41
42/// This function invokes undefined behavior when `ok` is `false`.
43/// In Debug and ReleaseSafe modes, calls to this function are always
44/// generated, and the `unreachable` statement triggers a panic.
45/// In ReleaseFast and ReleaseSmall modes, calls to this function can be
46/// optimized away.
1547pub fn assert(ok: bool) {
1648 if (!ok) {
1749 // In ReleaseFast test mode, we still want assert(false) to crash, so
1850 // we insert an explicit call to @panic instead of unreachable.
51 // TODO we should use `assertOrPanic` in tests and remove this logic.
1952 if (builtin.is_test) {
2053 @panic("assertion failure")
2154 } else {
......@@ -24,6 +57,14 @@ pub fn assert(ok: bool) {
2457 }
2558}
2659
60/// Call this function when you want to panic if the condition is not true.
61/// If `ok` is `false`, this function will panic in every release mode.
62pub fn assertOrPanic(ok: bool) {
63 if (!ok) {
64 @panic("assertion failure");
65 }
66}
67
2768var panicking = false;
2869/// This is the default panic implementation.
2970pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
......@@ -41,18 +82,13 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
4182 panicking = true;
4283 }
4384
44 %%io.stderr.printf(format ++ "\n", args);
45 %%writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty() %% false, 1);
46 %%io.stderr.flush();
85 const stderr = getStderrStream() %% os.abort();
86 stderr.print(format ++ "\n", args) %% os.abort();
87 writeStackTrace(stderr, &global_allocator, stderr_file.isTty(), 1) %% os.abort();
4788
4889 os.abort();
4990}
5091
51pub fn printStackTrace() -> %void {
52 %return writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty() %% false, 1);
53 %return io.stderr.flush();
54}
55
5692const GREEN = "\x1b[32;1m";
5793const WHITE = "\x1b[37;1m";
5894const DIM = "\x1b[2m";
......@@ -69,7 +105,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
69105 switch (builtin.object_format) {
70106 builtin.ObjectFormat.elf => {
71107 var stack_trace = ElfStackTrace {
72 .self_exe_stream = undefined,
108 .self_exe_file = undefined,
73109 .elf = undefined,
74110 .debug_info = undefined,
75111 .debug_abbrev = undefined,
......@@ -79,10 +115,10 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
79115 .compile_unit_list = ArrayList(CompileUnit).init(allocator),
80116 };
81117 const st = &stack_trace;
82 st.self_exe_stream = %return io.openSelfExe();
83 defer st.self_exe_stream.close();
118 st.self_exe_file = %return os.openSelfExe();
119 defer st.self_exe_file.close();
84120
85 %return st.elf.openStream(allocator, &st.self_exe_stream);
121 %return st.elf.openFile(allocator, &st.self_exe_file);
86122 defer st.elf.close();
87123
88124 st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
......@@ -109,7 +145,6 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
109145 const compile_unit = findCompileUnit(st, return_address) ?? {
110146 %return out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
111147 return_address);
112 %return out_stream.flush();
113148 continue;
114149 };
115150 const compile_unit_name = %return compile_unit.die.getAttrString(st, DW.AT_name);
......@@ -139,7 +174,6 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
139174 },
140175 else => return err,
141176 };
142 %return out_stream.flush();
143177 }
144178 },
145179 builtin.ObjectFormat.coff => {
......@@ -158,7 +192,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
158192}
159193
160194fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) -> %void {
161 var f = %return io.InStream.open(line_info.file_name, allocator);
195 var f = %return io.File.openRead(line_info.file_name, allocator);
162196 defer f.close();
163197 // TODO fstat and make sure that the file has the correct size
164198
......@@ -167,7 +201,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
167201 var column: usize = 1;
168202 var abs_index: usize = 0;
169203 while (true) {
170 const amt_read = %return f.read(buf[0..]);
204 const amt_read = %return f.in_stream.read(buf[0..]);
171205 const slice = buf[0..amt_read];
172206
173207 for (slice) |byte| {
......@@ -191,7 +225,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
191225}
192226
193227const ElfStackTrace = struct {
194 self_exe_stream: io.InStream,
228 self_exe_file: io.File,
195229 elf: elf.Elf,
196230 debug_info: &elf.SectionHeader,
197231 debug_abbrev: &elf.SectionHeader,
......@@ -205,7 +239,7 @@ const ElfStackTrace = struct {
205239 }
206240
207241 pub fn readString(self: &ElfStackTrace) -> %[]u8 {
208 return readStringRaw(self.allocator(), &self.self_exe_stream);
242 return readStringRaw(self.allocator(), &self.self_exe_file.in_stream);
209243 }
210244};
211245
......@@ -424,7 +458,7 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) -> %[]u8 {
424458
425459fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 {
426460 const pos = st.debug_str.offset + offset;
427 %return st.self_exe_stream.seekTo(pos);
461 %return st.self_exe_file.seekTo(pos);
428462 return st.readString();
429463}
430464
......@@ -533,7 +567,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
533567}
534568
535569fn parseAbbrevTable(st: &ElfStackTrace) -> %AbbrevTable {
536 const in_stream = &st.self_exe_stream;
570 const in_stream = &st.self_exe_file.in_stream;
537571 var result = AbbrevTable.init(st.allocator());
538572 while (true) {
539573 const abbrev_code = %return readULeb128(in_stream);
......@@ -568,7 +602,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&const AbbrevTable
568602 return &header.table;
569603 }
570604 }
571 %return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset);
605 %return st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset);
572606 %return st.abbrev_table_list.append(AbbrevTableHeader {
573607 .offset = abbrev_offset,
574608 .table = %return parseAbbrevTable(st),
......@@ -585,8 +619,8 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?&
585619}
586620
587621fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die {
588 const in_stream = &st.self_exe_stream;
589 const abbrev_code = %return readULeb128(in_stream);
622 const in_file = &st.self_exe_file;
623 const abbrev_code = %return readULeb128(&in_file.in_stream);
590624 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo;
591625
592626 var result = Die {
......@@ -598,7 +632,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -
598632 for (table_entry.attrs.toSliceConst()) |attr, i| {
599633 result.attrs.items[i] = Die.Attr {
600634 .id = attr.attr_id,
601 .value = %return parseFormValue(st.allocator(), &st.self_exe_stream, attr.form_id, is_64),
635 .value = %return parseFormValue(st.allocator(), &st.self_exe_file.in_stream, attr.form_id, is_64),
602636 };
603637 }
604638 return result;
......@@ -607,16 +641,16 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -
607641fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) -> %LineInfo {
608642 const compile_unit_cwd = %return compile_unit.die.getAttrString(st, DW.AT_comp_dir);
609643
610 const in_stream = &st.self_exe_stream;
644 const in_file = &st.self_exe_file;
611645 const debug_line_end = st.debug_line.offset + st.debug_line.size;
612646 var this_offset = st.debug_line.offset;
613647 var this_index: usize = 0;
614648
615649 while (this_offset < debug_line_end) : (this_index += 1) {
616 %return in_stream.seekTo(this_offset);
650 %return in_file.seekTo(this_offset);
617651
618652 var is_64: bool = undefined;
619 const unit_length = %return readInitialLength(in_stream, &is_64);
653 const unit_length = %return readInitialLength(&in_file.in_stream, &is_64);
620654 if (unit_length == 0)
621655 return error.MissingDebugInfo;
622656 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
......@@ -626,28 +660,28 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
626660 continue;
627661 }
628662
629 const version = %return in_stream.readInt(st.elf.is_big_endian, u16);
663 const version = %return in_file.in_stream.readInt(st.elf.is_big_endian, u16);
630664 if (version != 2) return error.InvalidDebugInfo;
631665
632 const prologue_length = %return in_stream.readInt(st.elf.is_big_endian, u32);
633 const prog_start_offset = (%return in_stream.getPos()) + prologue_length;
666 const prologue_length = %return in_file.in_stream.readInt(st.elf.is_big_endian, u32);
667 const prog_start_offset = (%return in_file.getPos()) + prologue_length;
634668
635 const minimum_instruction_length = %return in_stream.readByte();
669 const minimum_instruction_length = %return in_file.in_stream.readByte();
636670 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
637671
638 const default_is_stmt = (%return in_stream.readByte()) != 0;
639 const line_base = %return in_stream.readByteSigned();
672 const default_is_stmt = (%return in_file.in_stream.readByte()) != 0;
673 const line_base = %return in_file.in_stream.readByteSigned();
640674
641 const line_range = %return in_stream.readByte();
675 const line_range = %return in_file.in_stream.readByte();
642676 if (line_range == 0)
643677 return error.InvalidDebugInfo;
644678
645 const opcode_base = %return in_stream.readByte();
679 const opcode_base = %return in_file.in_stream.readByte();
646680
647681 const standard_opcode_lengths = %return st.allocator().alloc(u8, opcode_base - 1);
648682
649683 {var i: usize = 0; while (i < opcode_base - 1) : (i += 1) {
650 standard_opcode_lengths[i] = %return in_stream.readByte();
684 standard_opcode_lengths[i] = %return in_file.in_stream.readByte();
651685 }}
652686
653687 var include_directories = ArrayList([]u8).init(st.allocator());
......@@ -667,9 +701,9 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
667701 const file_name = %return st.readString();
668702 if (file_name.len == 0)
669703 break;
670 const dir_index = %return readULeb128(in_stream);
671 const mtime = %return readULeb128(in_stream);
672 const len_bytes = %return readULeb128(in_stream);
704 const dir_index = %return readULeb128(&in_file.in_stream);
705 const mtime = %return readULeb128(&in_file.in_stream);
706 const len_bytes = %return readULeb128(&in_file.in_stream);
673707 %return file_entries.append(FileEntry {
674708 .file_name = file_name,
675709 .dir_index = dir_index,
......@@ -678,42 +712,32 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
678712 });
679713 }
680714
681 %return in_stream.seekTo(prog_start_offset);
715 %return in_file.seekTo(prog_start_offset);
682716
683717 while (true) {
684 //const pos = (%return in_stream.getPos()) - this_offset;
685 //if (pos == 0x1a3) @breakpoint();
686 //%%io.stderr.printf("\n{x8}\n", pos);
687
688 const opcode = %return in_stream.readByte();
718 const opcode = %return in_file.in_stream.readByte();
689719
690720 var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash
691721 if (opcode == DW.LNS_extended_op) {
692 const op_size = %return readULeb128(in_stream);
722 const op_size = %return readULeb128(&in_file.in_stream);
693723 if (op_size < 1)
694724 return error.InvalidDebugInfo;
695 sub_op = %return in_stream.readByte();
725 sub_op = %return in_file.in_stream.readByte();
696726 switch (sub_op) {
697727 DW.LNE_end_sequence => {
698 //%%io.stdout.printf(" [0x{x8}] End Sequence\n", pos);
699728 prog.end_sequence = true;
700729 if (%return prog.checkLineMatch()) |info| return info;
701730 return error.MissingDebugInfo;
702731 },
703732 DW.LNE_set_address => {
704 const addr = %return in_stream.readInt(st.elf.is_big_endian, usize);
733 const addr = %return in_file.in_stream.readInt(st.elf.is_big_endian, usize);
705734 prog.address = addr;
706
707 //%%io.stdout.printf(" [0x{x8}] Extended opcode {}: set Address to 0x{x}\n",
708 // pos, sub_op, addr);
709735 },
710736 DW.LNE_define_file => {
711 //%%io.stdout.printf(" [0x{x8}] Define File\n", pos);
712
713737 const file_name = %return st.readString();
714 const dir_index = %return readULeb128(in_stream);
715 const mtime = %return readULeb128(in_stream);
716 const len_bytes = %return readULeb128(in_stream);
738 const dir_index = %return readULeb128(&in_file.in_stream);
739 const mtime = %return readULeb128(&in_file.in_stream);
740 const len_bytes = %return readULeb128(&in_file.in_stream);
717741 %return file_entries.append(FileEntry {
718742 .file_name = file_name,
719743 .dir_index = dir_index,
......@@ -723,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
723747 },
724748 else => {
725749 const fwd_amt = math.cast(isize, op_size - 1) %% return error.InvalidDebugInfo;
726 %return in_stream.seekForward(fwd_amt);
750 %return in_file.seekForward(fwd_amt);
727751 },
728752 }
729753 } else if (opcode >= opcode_base) {
......@@ -733,48 +757,32 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
733757 const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
734758 prog.line += inc_line;
735759 prog.address += inc_addr;
736 //%%io.stdout.printf(
737 // " [0x{x8}] Special opcode {}: advance Address by {} to 0x{x} and Line by {} to {}\n",
738 // pos, adjusted_opcode, inc_addr, prog.address, inc_line, prog.line);
739760 if (%return prog.checkLineMatch()) |info| return info;
740761 prog.basic_block = false;
741762 } else {
742763 switch (opcode) {
743764 DW.LNS_copy => {
744 //%%io.stdout.printf(" [0x{x8}] Copy\n", pos);
745
746765 if (%return prog.checkLineMatch()) |info| return info;
747766 prog.basic_block = false;
748767 },
749768 DW.LNS_advance_pc => {
750 const arg = %return readULeb128(in_stream);
769 const arg = %return readULeb128(&in_file.in_stream);
751770 prog.address += arg * minimum_instruction_length;
752
753 //%%io.stdout.printf(" [0x{x8}] Advance PC by {} to 0x{x}\n", pos, arg, prog.address);
754771 },
755772 DW.LNS_advance_line => {
756 const arg = %return readILeb128(in_stream);
773 const arg = %return readILeb128(&in_file.in_stream);
757774 prog.line += arg;
758
759 //%%io.stdout.printf(" [0x{x8}] Advance Line by {} to {}\n", pos, arg, prog.line);
760775 },
761776 DW.LNS_set_file => {
762 const arg = %return readULeb128(in_stream);
777 const arg = %return readULeb128(&in_file.in_stream);
763778 prog.file = arg;
764
765 //%%io.stdout.printf(" [0x{x8}] Set File Name to entry {} in the File Name Table\n",
766 // pos, arg);
767779 },
768780 DW.LNS_set_column => {
769 const arg = %return readULeb128(in_stream);
781 const arg = %return readULeb128(&in_file.in_stream);
770782 prog.column = arg;
771
772 //%%io.stdout.printf(" [0x{x8}] Set column to {}\n", pos, arg);
773783 },
774784 DW.LNS_negate_stmt => {
775785 prog.is_stmt = !prog.is_stmt;
776
777 //%%io.stdout.printf(" [0x{x8}] Set is_stmt to {}\n", pos, if (prog.is_stmt) u8(1) else u8(0));
778786 },
779787 DW.LNS_set_basic_block => {
780788 prog.basic_block = true;
......@@ -782,23 +790,18 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
782790 DW.LNS_const_add_pc => {
783791 const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
784792 prog.address += inc_addr;
785
786 //%%io.stdout.printf(" [0x{x8}] Advance PC by constant {} to 0x{x}\n",
787 // pos, inc_addr, prog.address);
788793 },
789794 DW.LNS_fixed_advance_pc => {
790 const arg = %return in_stream.readInt(st.elf.is_big_endian, u16);
795 const arg = %return in_file.in_stream.readInt(st.elf.is_big_endian, u16);
791796 prog.address += arg;
792797 },
793798 DW.LNS_set_prologue_end => {
794 //%%io.stdout.printf(" [0x{x8}] Set prologue_end to true\n", pos);
795799 },
796800 else => {
797801 if (opcode - 1 >= standard_opcode_lengths.len)
798802 return error.InvalidDebugInfo;
799 //%%io.stdout.printf(" [0x{x8}] unknown op code {}\n", pos, opcode);
800803 const len_bytes = standard_opcode_lengths[opcode - 1];
801 %return in_stream.seekForward(len_bytes);
804 %return in_file.seekForward(len_bytes);
802805 },
803806 }
804807 }
......@@ -815,30 +818,30 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
815818 var this_unit_offset = st.debug_info.offset;
816819 var cu_index: usize = 0;
817820 while (this_unit_offset < debug_info_end) {
818 %return st.self_exe_stream.seekTo(this_unit_offset);
821 %return st.self_exe_file.seekTo(this_unit_offset);
819822
820823 var is_64: bool = undefined;
821 const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64);
824 const unit_length = %return readInitialLength(&st.self_exe_file.in_stream, &is_64);
822825 if (unit_length == 0)
823826 return;
824827 const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
825828
826 const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16);
829 const version = %return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u16);
827830 if (version < 2 or version > 5) return error.InvalidDebugInfo;
828831
829832 const debug_abbrev_offset = if (is_64) {
830 %return st.self_exe_stream.readInt(st.elf.is_big_endian, u64)
833 %return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u64)
831834 } else {
832 %return st.self_exe_stream.readInt(st.elf.is_big_endian, u32)
835 %return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u32)
833836 };
834837
835 const address_size = %return st.self_exe_stream.readByte();
838 const address_size = %return st.self_exe_file.in_stream.readByte();
836839 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
837840
838 const compile_unit_pos = %return st.self_exe_stream.getPos();
841 const compile_unit_pos = %return st.self_exe_file.getPos();
839842 const abbrev_table = %return getAbbrevTable(st, debug_abbrev_offset);
840843
841 %return st.self_exe_stream.seekTo(compile_unit_pos);
844 %return st.self_exe_file.seekTo(compile_unit_pos);
842845
843846 const compile_unit_die = %return st.allocator().create(Die);
844847 *compile_unit_die = %return parseDie(st, abbrev_table, is_64);
std/elf.zig+61-59
......@@ -1,7 +1,9 @@
1const io = @import("io.zig");
2const math = @import("math/index.zig");
3const mem = @import("mem.zig");
4const debug = @import("debug.zig");
1const std = @import("index.zig");
2const io = std.io;
3const math = std.math;
4const mem = std.mem;
5const debug = std.debug;
6const InStream = std.stream.InStream;
57
68error InvalidFormat;
79
......@@ -62,7 +64,7 @@ pub const SectionHeader = struct {
6264};
6365
6466pub const Elf = struct {
65 in_stream: &io.InStream,
67 in_file: &io.File,
6668 auto_close_stream: bool,
6769 is_64: bool,
6870 is_big_endian: bool,
......@@ -75,44 +77,44 @@ pub const Elf = struct {
7577 string_section: &SectionHeader,
7678 section_headers: []SectionHeader,
7779 allocator: &mem.Allocator,
78 prealloc_stream: io.InStream,
80 prealloc_file: io.File,
7981
8082 /// Call close when done.
81 pub fn openFile(elf: &Elf, allocator: &mem.Allocator, path: []const u8) -> %void {
82 %return elf.prealloc_stream.open(path);
83 %return elf.openStream(allocator, &elf.prealloc_stream);
83 pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) -> %void {
84 %return elf.prealloc_file.open(path);
85 %return elf.openFile(allocator, &elf.prealloc_file);
8486 elf.auto_close_stream = true;
8587 }
8688
8789 /// Call close when done.
88 pub fn openStream(elf: &Elf, allocator: &mem.Allocator, stream: &io.InStream) -> %void {
90 pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) -> %void {
8991 elf.allocator = allocator;
90 elf.in_stream = stream;
92 elf.in_file = file;
9193 elf.auto_close_stream = false;
9294
9395 var magic: [4]u8 = undefined;
94 %return elf.in_stream.readNoEof(magic[0..]);
96 %return elf.in_file.in_stream.readNoEof(magic[0..]);
9597 if (!mem.eql(u8, magic, "\x7fELF")) return error.InvalidFormat;
9698
97 elf.is_64 = switch (%return elf.in_stream.readByte()) {
99 elf.is_64 = switch (%return elf.in_file.in_stream.readByte()) {
98100 1 => false,
99101 2 => true,
100102 else => return error.InvalidFormat,
101103 };
102104
103 elf.is_big_endian = switch (%return elf.in_stream.readByte()) {
105 elf.is_big_endian = switch (%return elf.in_file.in_stream.readByte()) {
104106 1 => false,
105107 2 => true,
106108 else => return error.InvalidFormat,
107109 };
108110
109 const version_byte = %return elf.in_stream.readByte();
111 const version_byte = %return elf.in_file.in_stream.readByte();
110112 if (version_byte != 1) return error.InvalidFormat;
111113
112114 // skip over padding
113 %return elf.in_stream.seekForward(9);
115 %return elf.in_file.seekForward(9);
114116
115 elf.file_type = switch (%return elf.in_stream.readInt(elf.is_big_endian, u16)) {
117 elf.file_type = switch (%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16)) {
116118 1 => FileType.Relocatable,
117119 2 => FileType.Executable,
118120 3 => FileType.Shared,
......@@ -120,7 +122,7 @@ pub const Elf = struct {
120122 else => return error.InvalidFormat,
121123 };
122124
123 elf.arch = switch (%return elf.in_stream.readInt(elf.is_big_endian, u16)) {
125 elf.arch = switch (%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16)) {
124126 0x02 => Arch.Sparc,
125127 0x03 => Arch.x86,
126128 0x08 => Arch.Mips,
......@@ -133,34 +135,34 @@ pub const Elf = struct {
133135 else => return error.InvalidFormat,
134136 };
135137
136 const elf_version = %return elf.in_stream.readInt(elf.is_big_endian, u32);
138 const elf_version = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
137139 if (elf_version != 1) return error.InvalidFormat;
138140
139141 if (elf.is_64) {
140 elf.entry_addr = %return elf.in_stream.readInt(elf.is_big_endian, u64);
141 elf.program_header_offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
142 elf.section_header_offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
142 elf.entry_addr = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
143 elf.program_header_offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
144 elf.section_header_offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
143145 } else {
144 elf.entry_addr = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
145 elf.program_header_offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
146 elf.section_header_offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
146 elf.entry_addr = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
147 elf.program_header_offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
148 elf.section_header_offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
147149 }
148150
149151 // skip over flags
150 %return elf.in_stream.seekForward(4);
152 %return elf.in_file.seekForward(4);
151153
152 const header_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
154 const header_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
153155 if ((elf.is_64 and header_size != 64) or
154156 (!elf.is_64 and header_size != 52))
155157 {
156158 return error.InvalidFormat;
157159 }
158160
159 const ph_entry_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
160 const ph_entry_count = %return elf.in_stream.readInt(elf.is_big_endian, u16);
161 const sh_entry_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
162 const sh_entry_count = %return elf.in_stream.readInt(elf.is_big_endian, u16);
163 elf.string_section_index = u64(%return elf.in_stream.readInt(elf.is_big_endian, u16));
161 const ph_entry_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
162 const ph_entry_count = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
163 const sh_entry_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
164 const sh_entry_count = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
165 elf.string_section_index = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16));
164166
165167 if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
166168
......@@ -169,12 +171,12 @@ pub const Elf = struct {
169171 const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count);
170172 const end_ph = %return math.add(u64, elf.program_header_offset, ph_byte_count);
171173
172 const stream_end = %return elf.in_stream.getEndPos();
174 const stream_end = %return elf.in_file.getEndPos();
173175 if (stream_end < end_sh or stream_end < end_ph) {
174176 return error.InvalidFormat;
175177 }
176178
177 %return elf.in_stream.seekTo(elf.section_header_offset);
179 %return elf.in_file.seekTo(elf.section_header_offset);
178180
179181 elf.section_headers = %return elf.allocator.alloc(SectionHeader, sh_entry_count);
180182 %defer elf.allocator.free(elf.section_headers);
......@@ -183,32 +185,32 @@ pub const Elf = struct {
183185 if (sh_entry_size != 64) return error.InvalidFormat;
184186
185187 for (elf.section_headers) |*section| {
186 section.name = %return elf.in_stream.readInt(elf.is_big_endian, u32);
187 section.sh_type = %return elf.in_stream.readInt(elf.is_big_endian, u32);
188 section.flags = %return elf.in_stream.readInt(elf.is_big_endian, u64);
189 section.addr = %return elf.in_stream.readInt(elf.is_big_endian, u64);
190 section.offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
191 section.size = %return elf.in_stream.readInt(elf.is_big_endian, u64);
192 section.link = %return elf.in_stream.readInt(elf.is_big_endian, u32);
193 section.info = %return elf.in_stream.readInt(elf.is_big_endian, u32);
194 section.addr_align = %return elf.in_stream.readInt(elf.is_big_endian, u64);
195 section.ent_size = %return elf.in_stream.readInt(elf.is_big_endian, u64);
188 section.name = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
189 section.sh_type = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
190 section.flags = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
191 section.addr = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
192 section.offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
193 section.size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
194 section.link = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
195 section.info = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
196 section.addr_align = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
197 section.ent_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
196198 }
197199 } else {
198200 if (sh_entry_size != 40) return error.InvalidFormat;
199201
200202 for (elf.section_headers) |*section| {
201203 // TODO (multiple occurences) allow implicit cast from %u32 -> %u64 ?
202 section.name = %return elf.in_stream.readInt(elf.is_big_endian, u32);
203 section.sh_type = %return elf.in_stream.readInt(elf.is_big_endian, u32);
204 section.flags = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
205 section.addr = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
206 section.offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
207 section.size = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
208 section.link = %return elf.in_stream.readInt(elf.is_big_endian, u32);
209 section.info = %return elf.in_stream.readInt(elf.is_big_endian, u32);
210 section.addr_align = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
211 section.ent_size = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
204 section.name = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
205 section.sh_type = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
206 section.flags = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
207 section.addr = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
208 section.offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
209 section.size = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
210 section.link = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
211 section.info = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
212 section.addr_align = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
213 section.ent_size = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
212214 }
213215 }
214216
......@@ -230,7 +232,7 @@ pub const Elf = struct {
230232 elf.allocator.free(elf.section_headers);
231233
232234 if (elf.auto_close_stream)
233 elf.in_stream.close();
235 elf.in_file.close();
234236 }
235237
236238 pub fn findSection(elf: &Elf, name: []const u8) -> %?&SectionHeader {
......@@ -238,15 +240,15 @@ pub const Elf = struct {
238240 if (section.sh_type == SHT_NULL) continue;
239241
240242 const name_offset = elf.string_section.offset + section.name;
241 %return elf.in_stream.seekTo(name_offset);
243 %return elf.in_file.seekTo(name_offset);
242244
243245 for (name) |expected_c| {
244 const target_c = %return elf.in_stream.readByte();
246 const target_c = %return elf.in_file.in_stream.readByte();
245247 if (target_c == 0 or expected_c != target_c) goto next_section;
246248 }
247249
248250 {
249 const null_byte = %return elf.in_stream.readByte();
251 const null_byte = %return elf.in_file.in_stream.readByte();
250252 if (null_byte == 0) return section;
251253 }
252254
......@@ -257,6 +259,6 @@ pub const Elf = struct {
257259 }
258260
259261 pub fn seekToSection(elf: &Elf, section: &SectionHeader) -> %void {
260 %return elf.in_stream.seekTo(section.offset);
262 %return elf.in_file.seekTo(section.offset);
261263 }
262264};
std/fmt/index.zig+40-68
......@@ -19,10 +19,10 @@ const State = enum { // TODO put inside format function and make sure the name a
1919};
2020
2121/// Renders fmt string with args, calling output with slices of bytes.
22/// Return false from output function and output will not be called again.
23/// Returns false if output ever returned false, true otherwise.
24pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
25 comptime fmt: []const u8, args: ...) -> bool
22/// If `output` returns an error, the error is returned from `format` and
23/// `output` is not called again.
24pub fn format(context: var, output: fn(@typeOf(context), []const u8)->%void,
25 comptime fmt: []const u8, args: ...) -> %void
2626{
2727 comptime var start_index = 0;
2828 comptime var state = State.Start;
......@@ -38,15 +38,13 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
3838 '{' => {
3939 // TODO if you make this an if statement with `and` then it breaks
4040 if (start_index < i) {
41 if (!output(context, fmt[start_index..i]))
42 return false;
41 %return output(context, fmt[start_index..i]);
4342 }
4443 state = State.OpenBrace;
4544 },
4645 '}' => {
4746 if (start_index < i) {
48 if (!output(context, fmt[start_index..i]))
49 return false;
47 %return output(context, fmt[start_index..i]);
5048 }
5149 state = State.CloseBrace;
5250 },
......@@ -58,8 +56,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
5856 start_index = i;
5957 },
6058 '}' => {
61 if (!formatValue(args[next_arg], context, output))
62 return false;
59 %return formatValue(args[next_arg], context, output);
6360 next_arg += 1;
6461 state = State.Start;
6562 start_index = i + 1;
......@@ -109,8 +106,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
109106 },
110107 State.Integer => switch (c) {
111108 '}' => {
112 if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
113 return false;
109 %return formatInt(args[next_arg], radix, uppercase, width, context, output);
114110 next_arg += 1;
115111 state = State.Start;
116112 start_index = i + 1;
......@@ -124,8 +120,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
124120 State.IntegerWidth => switch (c) {
125121 '}' => {
126122 width = comptime %%parseUnsigned(usize, fmt[width_start..i], 10);
127 if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
128 return false;
123 %return formatInt(args[next_arg], radix, uppercase, width, context, output);
129124 next_arg += 1;
130125 state = State.Start;
131126 start_index = i + 1;
......@@ -136,8 +131,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
136131 State.BufWidth => switch (c) {
137132 '}' => {
138133 width = comptime %%parseUnsigned(usize, fmt[width_start..i], 10);
139 if (!formatBuf(args[next_arg], width, context, output))
140 return false;
134 %return formatBuf(args[next_arg], width, context, output);
141135 next_arg += 1;
142136 state = State.Start;
143137 start_index = i + 1;
......@@ -147,8 +141,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
147141 },
148142 State.Character => switch (c) {
149143 '}' => {
150 if (!formatAsciiChar(args[next_arg], context, output))
151 return false;
144 %return formatAsciiChar(args[next_arg], context, output);
152145 next_arg += 1;
153146 state = State.Start;
154147 start_index = i + 1;
......@@ -166,14 +159,11 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
166159 }
167160 }
168161 if (start_index < fmt.len) {
169 if (!output(context, fmt[start_index..]))
170 return false;
162 %return output(context, fmt[start_index..]);
171163 }
172
173 return true;
174164}
175165
176pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
166pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void {
177167 const T = @typeOf(value);
178168 switch (@typeId(T)) {
179169 builtin.TypeId.Int => {
......@@ -203,8 +193,7 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
203193 }
204194 },
205195 builtin.TypeId.Error => {
206 if (!output(context, "error."))
207 return false;
196 %return output(context, "error.");
208197 return output(context, @errorName(value));
209198 },
210199 builtin.TypeId.Pointer => {
......@@ -223,27 +212,23 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
223212 }
224213}
225214
226pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
215pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void {
227216 return output(context, (&c)[0..1]);
228217}
229218
230219pub fn formatBuf(buf: []const u8, width: usize,
231 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
220 context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
232221{
233 if (!output(context, buf))
234 return false;
222 %return output(context, buf);
235223
236 var leftover_padding = if (width > buf.len) (width - buf.len) else return true;
224 var leftover_padding = if (width > buf.len) (width - buf.len) else return;
237225 const pad_byte: u8 = ' ';
238226 while (leftover_padding > 0) : (leftover_padding -= 1) {
239 if (!output(context, (&pad_byte)[0..1]))
240 return false;
227 %return output(context, (&pad_byte)[0..1]);
241228 }
242
243 return true;
244229}
245230
246pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
231pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void {
247232 var x = f64(value);
248233
249234 // Errol doesn't handle these special cases.
......@@ -251,8 +236,7 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
251236 return output(context, "NaN");
252237 }
253238 if (math.signbit(x)) {
254 if (!output(context, "-"))
255 return false;
239 %return output(context, "-");
256240 x = -x;
257241 }
258242 if (math.isPositiveInf(x)) {
......@@ -264,34 +248,27 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
264248
265249 var buffer: [32]u8 = undefined;
266250 const float_decimal = errol3(x, buffer[0..]);
267 if (!output(context, float_decimal.digits[0..1]))
268 return false;
269 if (!output(context, "."))
270 return false;
251 %return output(context, float_decimal.digits[0..1]);
252 %return output(context, ".");
271253 if (float_decimal.digits.len > 1) {
272254 const num_digits = if (@typeOf(value) == f32) {
273255 math.min(usize(9), float_decimal.digits.len)
274256 } else {
275257 float_decimal.digits.len
276258 };
277 if (!output(context, float_decimal.digits[1 .. num_digits]))
278 return false;
259 %return output(context, float_decimal.digits[1 .. num_digits]);
279260 } else {
280 if (!output(context, "0"))
281 return false;
261 %return output(context, "0");
282262 }
283263
284264 if (float_decimal.exp != 1) {
285 if (!output(context, "e"))
286 return false;
287 if (!formatInt(float_decimal.exp - 1, 10, false, 0, context, output))
288 return false;
265 %return output(context, "e");
266 %return formatInt(float_decimal.exp - 1, 10, false, 0, context, output);
289267 }
290 return true;
291268}
292269
293270pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
294 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
271 context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
295272{
296273 if (@typeOf(value).is_signed) {
297274 return formatIntSigned(value, base, uppercase, width, context, output);
......@@ -301,13 +278,12 @@ pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
301278}
302279
303280fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
304 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
281 context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
305282{
306283 const uint = @IntType(false, @typeOf(value).bit_count);
307284 if (value < 0) {
308285 const minus_sign: u8 = '-';
309 if (!output(context, (&minus_sign)[0..1]))
310 return false;
286 %return output(context, (&minus_sign)[0..1]);
311287 const new_value = uint(-(value + 1)) + 1;
312288 const new_width = if (width == 0) 0 else (width - 1);
313289 return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
......@@ -315,8 +291,7 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
315291 return formatIntUnsigned(uint(value), base, uppercase, width, context, output);
316292 } else {
317293 const plus_sign: u8 = '+';
318 if (!output(context, (&plus_sign)[0..1]))
319 return false;
294 %return output(context, (&plus_sign)[0..1]);
320295 const new_value = uint(value);
321296 const new_width = if (width == 0) 0 else (width - 1);
322297 return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
......@@ -324,7 +299,7 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
324299}
325300
326301fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
327 context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
302 context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
328303{
329304 // max_int_digits accounts for the minus sign. when printing an unsigned
330305 // number we don't need to do that.
......@@ -348,8 +323,7 @@ fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
348323 const zero_byte: u8 = '0';
349324 var leftover_padding = padding - index;
350325 while (true) {
351 if (!output(context, (&zero_byte)[0..1]))
352 return false;
326 %return output(context, (&zero_byte)[0..1]);
353327 leftover_padding -= 1;
354328 if (leftover_padding == 0)
355329 break;
......@@ -368,17 +342,16 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width:
368342 .out_buf = out_buf,
369343 .index = 0,
370344 };
371 _ = formatInt(value, base, uppercase, width, &context, formatIntCallback);
345 %%formatInt(value, base, uppercase, width, &context, formatIntCallback);
372346 return context.index;
373347}
374348const FormatIntBuf = struct {
375349 out_buf: []u8,
376350 index: usize,
377351};
378fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) -> bool {
352fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) -> %void {
379353 mem.copy(u8, context.out_buf[context.index..], bytes);
380354 context.index += bytes.len;
381 return true;
382355}
383356
384357pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) -> %T {
......@@ -440,28 +413,27 @@ const BufPrintContext = struct {
440413 remaining: []u8,
441414};
442415
443fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> bool {
416fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> %void {
444417 mem.copy(u8, context.remaining, bytes);
445418 context.remaining = context.remaining[bytes.len..];
446 return true;
447419}
448420
449421pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) -> []u8 {
450422 var context = BufPrintContext { .remaining = buf, };
451 _ = format(&context, bufPrintWrite, fmt, args);
423 %%format(&context, bufPrintWrite, fmt, args);
452424 return buf[0..buf.len - context.remaining.len];
453425}
454426
455427pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) -> %[]u8 {
456428 var size: usize = 0;
457 _ = format(&size, countSize, fmt, args);
429 // Cannot fail because `countSize` cannot fail.
430 %%format(&size, countSize, fmt, args);
458431 const buf = %return allocator.alloc(u8, size);
459432 return bufPrint(buf, fmt, args);
460433}
461434
462fn countSize(size: &usize, bytes: []const u8) -> bool {
435fn countSize(size: &usize, bytes: []const u8) -> %void {
463436 *size += bytes.len;
464 return true;
465437}
466438
467439test "buf print int" {
std/heap.zig created+158
......@@ -0,0 +1,158 @@
1const debug = @import("debug.zig");
2const assert = debug.assert;
3const mem = @import("mem.zig");
4const os = @import("os/index.zig");
5const builtin = @import("builtin");
6const Os = builtin.Os;
7const c = @import("c/index.zig");
8
9const Allocator = mem.Allocator;
10
11error OutOfMemory;
12
13pub var c_allocator = Allocator {
14 .allocFn = cAlloc,
15 .reallocFn = cRealloc,
16 .freeFn = cFree,
17};
18
19fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
20 if (c.malloc(usize(n))) |mem| {
21 @ptrCast(&u8, mem)[0..n]
22 } else {
23 error.OutOfMemory
24 }
25}
26
27fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
28 if (new_size <= old_mem.len) {
29 old_mem[0..new_size]
30 } else {
31 const old_ptr = @ptrCast(&c_void, old_mem.ptr);
32 if (c.realloc(old_ptr, usize(new_size))) |mem| {
33 @ptrCast(&u8, mem)[0..new_size]
34 } else {
35 error.OutOfMemory
36 }
37 }
38}
39
40fn cFree(self: &Allocator, old_mem: []u8) {
41 const old_ptr = @ptrCast(&c_void, old_mem.ptr);
42 c.free(old_ptr);
43}
44
45pub const IncrementingAllocator = struct {
46 allocator: Allocator,
47 bytes: []u8,
48 end_index: usize,
49 heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
50
51 fn init(capacity: usize) -> %IncrementingAllocator {
52 switch (builtin.os) {
53 Os.linux, Os.darwin, Os.macosx, Os.ios => {
54 const p = os.posix;
55 const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
56 p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
57 if (addr == p.MAP_FAILED) {
58 return error.OutOfMemory;
59 }
60 return IncrementingAllocator {
61 .allocator = Allocator {
62 .allocFn = alloc,
63 .reallocFn = realloc,
64 .freeFn = free,
65 },
66 .bytes = @intToPtr(&u8, addr)[0..capacity],
67 .end_index = 0,
68 .heap_handle = {},
69 };
70 },
71 Os.windows => {
72 const heap_handle = os.windows.GetProcessHeap() ?? return error.OutOfMemory;
73 const ptr = os.windows.HeapAlloc(heap_handle, 0, capacity) ?? return error.OutOfMemory;
74 return IncrementingAllocator {
75 .allocator = Allocator {
76 .allocFn = alloc,
77 .reallocFn = realloc,
78 .freeFn = free,
79 },
80 .bytes = @ptrCast(&u8, ptr)[0..capacity],
81 .end_index = 0,
82 .heap_handle = heap_handle,
83 };
84 },
85 else => @compileError("Unsupported OS"),
86 }
87 }
88
89 fn deinit(self: &IncrementingAllocator) {
90 switch (builtin.os) {
91 Os.linux, Os.darwin, Os.macosx, Os.ios => {
92 _ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
93 },
94 Os.windows => {
95 _ = os.windows.HeapFree(self.heap_handle, 0, @ptrCast(os.windows.LPVOID, self.bytes.ptr));
96 },
97 else => @compileError("Unsupported OS"),
98 }
99 }
100
101 fn reset(self: &IncrementingAllocator) {
102 self.end_index = 0;
103 }
104
105 fn bytesLeft(self: &const IncrementingAllocator) -> usize {
106 return self.bytes.len - self.end_index;
107 }
108
109 fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
110 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
111 const addr = @ptrToInt(&self.bytes[self.end_index]);
112 const rem = @rem(addr, alignment);
113 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
114 const adjusted_index = self.end_index + march_forward_bytes;
115 const new_end_index = adjusted_index + n;
116 if (new_end_index > self.bytes.len) {
117 return error.OutOfMemory;
118 }
119 const result = self.bytes[adjusted_index .. new_end_index];
120 self.end_index = new_end_index;
121 return result;
122 }
123
124 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
125 if (new_size <= old_mem.len) {
126 return old_mem[0..new_size];
127 } else {
128 const result = %return alloc(allocator, new_size, alignment);
129 mem.copy(u8, result, old_mem);
130 return result;
131 }
132 }
133
134 fn free(allocator: &Allocator, bytes: []u8) {
135 // Do nothing. That's the point of an incrementing allocator.
136 }
137};
138
139test "IncrementingAllocator" {
140 const total_bytes = 100 * 1024 * 1024;
141 var inc_allocator = %%IncrementingAllocator.init(total_bytes);
142 defer inc_allocator.deinit();
143
144 const allocator = &inc_allocator.allocator;
145 const slice = %%allocator.alloc(&i32, 100);
146
147 for (slice) |*item, i| {
148 *item = %%allocator.create(i32);
149 **item = i32(i);
150 }
151
152 assert(inc_allocator.bytesLeft() == total_bytes - @sizeOf(i32) * 100 - @sizeOf(usize) * 100);
153
154 inc_allocator.reset();
155
156 assert(inc_allocator.bytesLeft() == total_bytes);
157}
158
std/index.zig+2
......@@ -15,6 +15,7 @@ pub const elf = @import("elf.zig");
1515pub const empty_import = @import("empty.zig");
1616pub const endian = @import("endian.zig");
1717pub const fmt = @import("fmt/index.zig");
18pub const heap = @import("heap.zig");
1819pub const io = @import("io.zig");
1920pub const math = @import("math/index.zig");
2021pub const mem = @import("mem.zig");
......@@ -45,6 +46,7 @@ test "std" {
4546 _ = @import("io.zig");
4647 _ = @import("math/index.zig");
4748 _ = @import("mem.zig");
49 _ = @import("heap.zig");
4850 _ = @import("net.zig");
4951 _ = @import("os/index.zig");
5052 _ = @import("rand.zig");
std/io.zig+278-352
......@@ -1,3 +1,4 @@
1const std = @import("index.zig");
12const builtin = @import("builtin");
23const Os = builtin.Os;
34const system = switch(builtin.os) {
......@@ -6,41 +7,19 @@ const system = switch(builtin.os) {
67 Os.windows => @import("os/windows/index.zig"),
78 else => @compileError("Unsupported OS"),
89};
9const c = @import("c/index.zig");
10const c = std.c;
1011
11const math = @import("math/index.zig");
12const debug = @import("debug.zig");
12const math = std.math;
13const debug = std.debug;
1314const assert = debug.assert;
14const os = @import("os/index.zig");
15const mem = @import("mem.zig");
16const Buffer = @import("buffer.zig").Buffer;
17const fmt = @import("fmt/index.zig");
15const os = std.os;
16const mem = std.mem;
17const Buffer = std.Buffer;
18const fmt = std.fmt;
1819
1920const is_posix = builtin.os != builtin.Os.windows;
2021const is_windows = builtin.os == builtin.Os.windows;
2122
22pub var stdin = InStream {
23 .fd = if (is_posix) system.STDIN_FILENO else {},
24 .handle_id = if (is_windows) system.STD_INPUT_HANDLE else {},
25 .handle = if (is_windows) null else {},
26};
27
28pub var stdout = OutStream {
29 .fd = if (is_posix) system.STDOUT_FILENO else {},
30 .handle_id = if (is_windows) system.STD_OUTPUT_HANDLE else {},
31 .handle = if (is_windows) null else {},
32 .buffer = undefined,
33 .index = 0,
34};
35
36pub var stderr = OutStream {
37 .fd = if (is_posix) system.STDERR_FILENO else {},
38 .handle_id = if (is_windows) system.STD_ERROR_HANDLE else {},
39 .handle = if (is_windows) null else {},
40 .buffer = undefined,
41 .index = 0,
42};
43
4423/// The function received invalid input at runtime. An Invalid error means a
4524/// bug in the program that called the function.
4625error Invalid;
......@@ -63,306 +42,125 @@ error PathNotFound;
6342error OutOfMemory;
6443error Unseekable;
6544error EndOfFile;
66error NoStdHandles;
67
68pub const OutStream = struct {
69 fd: if (is_posix) i32 else void,
70 handle_id: if (is_windows) system.DWORD else void,
71 handle: if (is_windows) ?system.HANDLE else void,
72 buffer: [os.page_size]u8,
73 index: usize,
74
75 /// Calls ::openMode with 0o666 for the mode.
76 pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %OutStream {
77 return openMode(path, 0o666, allocator);
7845
79 }
80
81 /// `path` may need to be copied in memory to add a null terminating byte. In this case
82 /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
83 /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
84 /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
85 /// Call close to clean up.
86 pub fn openMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) -> %OutStream {
87 if (is_posix) {
88 const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC;
89 const fd = %return os.posixOpen(path, flags, mode, allocator);
90 return OutStream {
91 .fd = fd,
92 .handle = {},
93 .handle_id = {},
94 .index = 0,
95 .buffer = undefined,
96 };
97 } else if (is_windows) {
98 const handle = %return os.windowsOpen(path, system.GENERIC_WRITE,
99 system.FILE_SHARE_WRITE|system.FILE_SHARE_READ|system.FILE_SHARE_DELETE,
100 system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL, allocator);
101 return OutStream {
102 .fd = {},
103 .handle = handle,
104 .handle_id = undefined,
105 .index = 0,
106 .buffer = undefined,
107 };
108
109 } else {
110 unreachable;
111 }
112
113 }
114
115 pub fn writeByte(self: &OutStream, b: u8) -> %void {
116 if (self.buffer.len == self.index) %return self.flush();
117 self.buffer[self.index] = b;
118 self.index += 1;
119 }
120
121 pub fn write(self: &OutStream, bytes: []const u8) -> %void {
122 if (bytes.len >= self.buffer.len) {
123 %return self.flush();
124 return self.unbufferedWrite(bytes);
125 }
126
127 var src_index: usize = 0;
128
129 while (src_index < bytes.len) {
130 const dest_space_left = self.buffer.len - self.index;
131 const copy_amt = math.min(dest_space_left, bytes.len - src_index);
132 mem.copy(u8, self.buffer[self.index..], bytes[src_index..src_index + copy_amt]);
133 self.index += copy_amt;
134 assert(self.index <= self.buffer.len);
135 if (self.index == self.buffer.len) {
136 %return self.flush();
137 }
138 src_index += copy_amt;
139 }
140 }
141
142 /// Calls print and then flushes the buffer.
143 pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
144 %return self.print(format, args);
145 %return self.flush();
146 }
147
148 /// Does not flush the buffer.
149 pub fn print(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
150 var context = PrintContext {
151 .self = self,
152 .result = {},
153 };
154 _ = fmt.format(&context, printOutput, format, args);
155 return context.result;
156 }
157 const PrintContext = struct {
158 self: &OutStream,
159 result: %void,
46pub fn getStdErr() -> %File {
47 const handle = if (is_windows) {
48 %return os.windowsGetStdHandle(system.STD_ERROR_HANDLE)
49 } else if (is_posix) {
50 system.STDERR_FILENO
51 } else {
52 unreachable
16053 };
161 fn printOutput(context: &PrintContext, bytes: []const u8) -> bool {
162 context.self.write(bytes) %% |err| {
163 context.result = err;
164 return false;
165 };
166 return true;
167 }
168
169 pub fn flush(self: &OutStream) -> %void {
170 if (self.index == 0)
171 return;
172
173 %return self.unbufferedWrite(self.buffer[0..self.index]);
174 self.index = 0;
175 }
176
177 pub fn close(self: &OutStream) {
178 assert(self.index == 0); // unflushed buffer
179 if (is_posix) {
180 os.posixClose(self.fd);
181 } else if (is_windows) {
182 os.windowsClose(%%self.getHandle());
183 } else {
184 unreachable;
185 }
186 }
54 return File.openHandle(handle);
55}
18756
188 pub fn isTty(self: &OutStream) -> %bool {
189 if (is_posix) {
190 if (builtin.link_libc) {
191 return c.isatty(self.fd) != 0;
192 } else {
193 return system.isatty(self.fd);
194 }
195 } else if (is_windows) {
196 return os.windowsIsTty(%return self.getHandle());
197 } else {
198 unreachable;
199 }
200 }
57pub fn getStdOut() -> %File {
58 const handle = if (is_windows) {
59 %return os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE)
60 } else if (is_posix) {
61 system.STDOUT_FILENO
62 } else {
63 unreachable
64 };
65 return File.openHandle(handle);
66}
20167
202 fn getHandle(self: &OutStream) -> %system.HANDLE {
203 if (self.handle) |handle| return handle;
204 if (system.GetStdHandle(self.handle_id)) |handle| {
205 if (handle == system.INVALID_HANDLE_VALUE) {
206 const err = system.GetLastError();
207 return switch (err) {
208 else => os.unexpectedErrorWindows(err),
209 };
210 }
211 self.handle = handle;
212 return handle;
213 } else {
214 return error.NoStdHandles;
215 }
216 }
68pub fn getStdIn() -> %File {
69 const handle = if (is_windows) {
70 %return os.windowsGetStdHandle(system.STD_INPUT_HANDLE)
71 } else if (is_posix) {
72 system.STDIN_FILENO
73 } else {
74 unreachable
75 };
76 return File.openHandle(handle);
77}
21778
218 fn unbufferedWrite(self: &OutStream, bytes: []const u8) -> %void {
219 if (is_posix) {
220 %return os.posixWrite(self.fd, bytes);
221 } else if (is_windows) {
222 const handle = %return self.getHandle();
223 %return os.windowsWrite(handle, bytes);
224 } else {
225 @compileError("Unsupported OS");
226 }
227 }
79pub const File = struct {
80 /// The OS-specific file descriptor or file handle.
81 handle: os.FileHandle,
22882
229};
83 /// A file has the `InStream` trait
84 in_stream: InStream,
23085
231// TODO created a BufferedInStream struct and move some of this code there
232// BufferedInStream API goes on top of minimal InStream API.
233pub const InStream = struct {
234 fd: if (is_posix) i32 else void,
235 handle_id: if (is_windows) system.DWORD else void,
236 handle: if (is_windows) ?system.HANDLE else void,
86 /// A file has the `OutStream` trait
87 out_stream: OutStream,
23788
23889 /// `path` may need to be copied in memory to add a null terminating byte. In this case
23990 /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
24091 /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
24192 /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
24293 /// Call close to clean up.
243 pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %InStream {
94 pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) -> %File {
24495 if (is_posix) {
24596 const flags = system.O_LARGEFILE|system.O_RDONLY;
24697 const fd = %return os.posixOpen(path, flags, 0, allocator);
247 return InStream {
248 .fd = fd,
249 .handle_id = {},
250 .handle = {},
251 };
98 return openHandle(fd);
25299 } else if (is_windows) {
253100 const handle = %return os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ,
254101 system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator);
255 return InStream {
256 .fd = {},
257 .handle_id = undefined,
258 .handle = handle,
259 };
102 return openHandle(handle);
260103 } else {
261104 unreachable;
262105 }
263106 }
264107
265 /// Upon success, the stream is in an uninitialized state. To continue using it,
266 /// you must use the open() function.
267 pub fn close(self: &InStream) {
268 if (is_posix) {
269 os.posixClose(self.fd);
270 } else if (is_windows) {
271 os.windowsClose(%%self.getHandle());
272 } else {
273 unreachable;
274 }
108 /// Calls `openWriteMode` with 0o666 for the mode.
109 pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) -> %File {
110 return openWriteMode(path, 0o666, allocator);
111
275112 }
276113
277 /// Returns the number of bytes read. If the number read is smaller than buf.len, then
278 /// the stream reached End Of File.
279 pub fn read(self: &InStream, buf: []u8) -> %usize {
114 /// `path` may need to be copied in memory to add a null terminating byte. In this case
115 /// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
116 /// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
117 /// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
118 /// Call close to clean up.
119 pub fn openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) -> %File {
280120 if (is_posix) {
281 var index: usize = 0;
282 while (index < buf.len) {
283 const amt_read = system.read(self.fd, &buf[index], buf.len - index);
284 const read_err = system.getErrno(amt_read);
285 if (read_err > 0) {
286 switch (read_err) {
287 system.EINTR => continue,
288 system.EINVAL => unreachable,
289 system.EFAULT => unreachable,
290 system.EBADF => return error.BadFd,
291 system.EIO => return error.Io,
292 else => return os.unexpectedErrorPosix(read_err),
293 }
294 }
295 if (amt_read == 0) return index;
296 index += amt_read;
297 }
298 return index;
121 const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC;
122 const fd = %return os.posixOpen(path, flags, mode, allocator);
123 return openHandle(fd);
299124 } else if (is_windows) {
300 const handle = %return self.getHandle();
301 var index: usize = 0;
302 while (index < buf.len) {
303 const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buf.len - index));
304 var amt_read: system.DWORD = undefined;
305 if (system.ReadFile(handle, @ptrCast(&c_void, &buf[index]), want_read_count, &amt_read, null) == 0) {
306 const err = system.GetLastError();
307 return switch (err) {
308 system.ERROR.OPERATION_ABORTED => continue,
309 system.ERROR.BROKEN_PIPE => return index,
310 else => os.unexpectedErrorWindows(err),
311 };
312 }
313 if (amt_read == 0) return index;
314 index += amt_read;
315 }
316 return index;
125 const handle = %return os.windowsOpen(path, system.GENERIC_WRITE,
126 system.FILE_SHARE_WRITE|system.FILE_SHARE_READ|system.FILE_SHARE_DELETE,
127 system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL, allocator);
128 return openHandle(handle);
317129 } else {
318130 unreachable;
319131 }
320 }
321132
322 pub fn readNoEof(is: &InStream, buf: []u8) -> %void {
323 const amt_read = %return is.read(buf);
324 if (amt_read < buf.len) return error.EndOfFile;
325133 }
326134
327 pub fn readByte(is: &InStream) -> %u8 {
328 var result: [1]u8 = undefined;
329 %return is.readNoEof(result[0..]);
330 return result[0];
331 }
332
333 pub fn readByteSigned(is: &InStream) -> %i8 {
334 var result: [1]i8 = undefined;
335 %return is.readNoEof(([]u8)(result[0..]));
336 return result[0];
337 }
338
339 pub fn readIntLe(is: &InStream, comptime T: type) -> %T {
340 is.readInt(false, T)
135 pub fn openHandle(handle: os.FileHandle) -> File {
136 return File {
137 .handle = handle,
138 .out_stream = OutStream {
139 .writeFn = writeFn,
140 },
141 .in_stream = InStream {
142 .readFn = readFn,
143 },
144 };
341145 }
342146
343 pub fn readIntBe(is: &InStream, comptime T: type) -> %T {
344 is.readInt(true, T)
345 }
346147
347 pub fn readInt(is: &InStream, is_be: bool, comptime T: type) -> %T {
348 var bytes: [@sizeOf(T)]u8 = undefined;
349 %return is.readNoEof(bytes[0..]);
350 return mem.readInt(bytes, T, is_be);
148 /// Upon success, the stream is in an uninitialized state. To continue using it,
149 /// you must use the open() function.
150 pub fn close(self: &File) {
151 os.close(self.handle);
152 self.handle = undefined;
351153 }
352154
353 pub fn readVarInt(is: &InStream, is_be: bool, comptime T: type, size: usize) -> %T {
354 assert(size <= @sizeOf(T));
355 assert(size <= 8);
356 var input_buf: [8]u8 = undefined;
357 const input_slice = input_buf[0..size];
358 %return is.readNoEof(input_slice);
359 return mem.readInt(input_slice, T, is_be);
155 /// Calls `os.isTty` on `self.handle`.
156 pub fn isTty(self: &File) -> bool {
157 return os.isTty(self.handle);
360158 }
361159
362 pub fn seekForward(is: &InStream, amount: isize) -> %void {
160 pub fn seekForward(self: &File, amount: isize) -> %void {
363161 switch (builtin.os) {
364162 Os.linux, Os.darwin => {
365 const result = system.lseek(is.fd, amount, system.SEEK_CUR);
163 const result = system.lseek(self.handle, amount, system.SEEK_CUR);
366164 const err = system.getErrno(result);
367165 if (err > 0) {
368166 return switch (err) {
......@@ -379,10 +177,10 @@ pub const InStream = struct {
379177 }
380178 }
381179
382 pub fn seekTo(is: &InStream, pos: usize) -> %void {
180 pub fn seekTo(self: &File, pos: usize) -> %void {
383181 switch (builtin.os) {
384182 Os.linux, Os.darwin => {
385 const result = system.lseek(is.fd, @bitCast(isize, pos), system.SEEK_SET);
183 const result = system.lseek(self.handle, @bitCast(isize, pos), system.SEEK_SET);
386184 const err = system.getErrno(result);
387185 if (err > 0) {
388186 return switch (err) {
......@@ -399,10 +197,10 @@ pub const InStream = struct {
399197 }
400198 }
401199
402 pub fn getPos(is: &InStream) -> %usize {
200 pub fn getPos(self: &File) -> %usize {
403201 switch (builtin.os) {
404202 Os.linux, Os.darwin => {
405 const result = system.lseek(is.fd, 0, system.SEEK_CUR);
203 const result = system.lseek(self.handle, 0, system.SEEK_CUR);
406204 const err = system.getErrno(result);
407205 if (err > 0) {
408206 return switch (err) {
......@@ -420,9 +218,9 @@ pub const InStream = struct {
420218 }
421219 }
422220
423 pub fn getEndPos(is: &InStream) -> %usize {
221 pub fn getEndPos(self: &File) -> %usize {
424222 var stat: system.Stat = undefined;
425 const err = system.getErrno(system.fstat(is.fd, &stat));
223 const err = system.getErrno(system.fstat(self.handle, &stat));
426224 if (err > 0) {
427225 return switch (err) {
428226 system.EBADF => error.BadFd,
......@@ -434,89 +232,217 @@ pub const InStream = struct {
434232 return usize(stat.size);
435233 }
436234
437 pub fn readAll(is: &InStream, buf: &Buffer) -> %void {
438 %return buf.resize(os.page_size);
235 fn readFn(in_stream: &InStream, buffer: []u8) -> %usize {
236 const self = @fieldParentPtr(File, "in_stream", in_stream);
237 if (is_posix) {
238 var index: usize = 0;
239 while (index < buffer.len) {
240 const amt_read = system.read(self.handle, &buffer[index], buffer.len - index);
241 const read_err = system.getErrno(amt_read);
242 if (read_err > 0) {
243 switch (read_err) {
244 system.EINTR => continue,
245 system.EINVAL => unreachable,
246 system.EFAULT => unreachable,
247 system.EBADF => return error.BadFd,
248 system.EIO => return error.Io,
249 else => return os.unexpectedErrorPosix(read_err),
250 }
251 }
252 if (amt_read == 0) return index;
253 index += amt_read;
254 }
255 return index;
256 } else if (is_windows) {
257 var index: usize = 0;
258 while (index < buffer.len) {
259 const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buffer.len - index));
260 var amt_read: system.DWORD = undefined;
261 if (system.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) {
262 const err = system.GetLastError();
263 return switch (err) {
264 system.ERROR.OPERATION_ABORTED => continue,
265 system.ERROR.BROKEN_PIPE => return index,
266 else => os.unexpectedErrorWindows(err),
267 };
268 }
269 if (amt_read == 0) return index;
270 index += amt_read;
271 }
272 return index;
273 } else {
274 unreachable;
275 }
276 }
277
278 fn writeFn(out_stream: &OutStream, bytes: []const u8) -> %void {
279 const self = @fieldParentPtr(File, "out_stream", out_stream);
280 if (is_posix) {
281 %return os.posixWrite(self.handle, bytes);
282 } else if (is_windows) {
283 %return os.windowsWrite(self.handle, bytes);
284 } else {
285 @compileError("Unsupported OS");
286 }
287 }
288
289};
290
291/// `path` may need to be copied in memory to add a null terminating byte. In this case
292/// a fixed size buffer of size `std.os.max_noalloc_path_len` is an attempted solution. If the fixed
293/// size buffer is too small, and the provided allocator is null, `error.NameTooLong` is returned.
294/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
295pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) -> %void {
296 var file = %return File.openWrite(path, allocator);
297 defer file.close();
298 %return file.out_stream.write(data);
299}
300
301error StreamTooLong;
302error EndOfStream;
303
304pub const InStream = struct {
305 /// Return the number of bytes read. If the number read is smaller than buf.len, it
306 /// means the stream reached the end. Reaching the end of a stream is not an error
307 /// condition.
308 readFn: fn(self: &InStream, buffer: []u8) -> %usize,
309
310 /// Replaces `buffer` contents by reading from the stream until it is finished.
311 /// If `buffer.len()` woould exceed `max_size`, `error.StreamTooLong` is returned and
312 /// the contents read from the stream are lost.
313 pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) -> %void {
314 %return buffer.resize(0);
439315
440316 var actual_buf_len: usize = 0;
441317 while (true) {
442 const dest_slice = buf.toSlice()[actual_buf_len..];
443 const bytes_read = %return is.read(dest_slice);
318 const dest_slice = buffer.toSlice()[actual_buf_len..];
319 const bytes_read = %return self.readFn(self, dest_slice);
444320 actual_buf_len += bytes_read;
445321
446322 if (bytes_read != dest_slice.len) {
447 return buf.resize(actual_buf_len);
323 buffer.shrink(actual_buf_len);
324 return;
448325 }
449326
450 %return buf.resize(actual_buf_len + os.page_size);
327 const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
328 if (new_buf_size == actual_buf_len)
329 return error.StreamTooLong;
330 %return buffer.resize(new_buf_size);
451331 }
452332 }
453333
454 pub fn readLine(is: &InStream, buf: &Buffer) -> %void {
334 /// Allocates enough memory to hold all the contents of the stream. If the allocated
335 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
336 /// Caller owns returned memory.
337 /// If this function returns an error, the contents from the stream read so far are lost.
338 pub fn readAllAlloc(self: &InStream, allocator: &mem.Allocator, max_size: usize) -> %[]u8 {
339 var buf = Buffer.initNull(allocator);
340 defer buf.deinit();
341
342 %return self.readAllBuffer(self, &buf, max_size);
343 return buf.toOwnedSlice();
344 }
345
346 /// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
347 /// Does not include the delimiter in the result.
348 /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
349 /// read from the stream so far are lost.
350 pub fn readUntilDelimiterBuffer(self: &InStream, buffer: &Buffer, delimiter: u8, max_size: usize) -> %void {
455351 %return buf.resize(0);
456352
457353 while (true) {
458 var byte: u8 = %return is.readByte();
459 %return buf.appendByte(byte);
354 var byte: u8 = %return self.readByte();
460355
461 if (buf.endsWith(os.line_sep)) {
462 break;
356 if (byte == delimiter) {
357 return;
463358 }
464 }
465 }
466359
467 pub fn isTty(self: &InStream) -> %bool {
468 if (is_posix) {
469 if (builtin.link_libc) {
470 return c.isatty(self.fd) != 0;
471 } else {
472 return system.isatty(self.fd);
360 if (buf.len() == max_size) {
361 return error.StreamTooLong;
473362 }
474 } else if (is_windows) {
475 return os.windowsIsTty(%return self.getHandle());
476 } else {
477 @compileError("Unsupported OS");
363
364 %return buf.appendByte(byte);
478365 }
479366 }
480367
481 fn getHandle(self: &InStream) -> %system.HANDLE {
482 if (self.handle) |handle| return handle;
483 if (system.GetStdHandle(self.handle_id)) |handle| {
484 if (handle == system.INVALID_HANDLE_VALUE) {
485 const err = system.GetLastError();
486 return switch (err) {
487 else => os.unexpectedErrorWindows(err),
488 };
489 }
490 self.handle = handle;
491 return handle;
492 } else {
493 return error.NoStdHandles;
494 }
368 /// Allocates enough memory to read until `delimiter`. If the allocated
369 /// memory would be greater than `max_size`, returns `error.StreamTooLong`.
370 /// Caller owns returned memory.
371 /// If this function returns an error, the contents from the stream read so far are lost.
372 pub fn readUntilDelimiterAlloc(self: &InStream, allocator: &mem.Allocator,
373 delimiter: u8, max_size: usize) -> %[]u8
374 {
375 var buf = Buffer.initNull(allocator);
376 defer buf.deinit();
377
378 %return self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
379 return buf.toOwnedSlice();
380 }
381
382 /// Returns the number of bytes read. If the number read is smaller than buf.len, it
383 /// means the stream reached the end. Reaching the end of a stream is not an error
384 /// condition.
385 pub fn read(self: &InStream, buffer: []u8) -> %usize {
386 return self.readFn(self, buffer);
387 }
388
389 /// Same as `read` but end of stream returns `error.EndOfStream`.
390 pub fn readNoEof(self: &InStream, buf: []u8) -> %void {
391 const amt_read = %return self.read(buf);
392 if (amt_read < buf.len) return error.EndOfStream;
393 }
394
395 /// Reads 1 byte from the stream or returns `error.EndOfStream`.
396 pub fn readByte(self: &InStream) -> %u8 {
397 var result: [1]u8 = undefined;
398 %return self.readNoEof(result[0..]);
399 return result[0];
495400 }
401
402 /// Same as `readByte` except the returned byte is signed.
403 pub fn readByteSigned(self: &InStream) -> %i8 {
404 return @bitCast(i8, %return self.readByte());
405 }
406
407 pub fn readIntLe(self: &InStream, comptime T: type) -> %T {
408 return self.readInt(false, T);
409 }
410
411 pub fn readIntBe(self: &InStream, comptime T: type) -> %T {
412 return self.readInt(true, T);
413 }
414
415 pub fn readInt(self: &InStream, is_be: bool, comptime T: type) -> %T {
416 var bytes: [@sizeOf(T)]u8 = undefined;
417 %return self.readNoEof(bytes[0..]);
418 return mem.readInt(bytes, T, is_be);
419 }
420
421 pub fn readVarInt(self: &InStream, is_be: bool, comptime T: type, size: usize) -> %T {
422 assert(size <= @sizeOf(T));
423 assert(size <= 8);
424 var input_buf: [8]u8 = undefined;
425 const input_slice = input_buf[0..size];
426 %return self.readNoEof(input_slice);
427 return mem.readInt(input_slice, T, is_be);
428 }
429
430
496431};
497432
498pub fn openSelfExe() -> %InStream {
499 switch (builtin.os) {
500 Os.linux => {
501 return InStream.open("/proc/self/exe", null);
502 },
503 Os.darwin => {
504 debug.panic("TODO: openSelfExe on Darwin");
505 },
506 else => @compileError("Unsupported OS"),
433pub const OutStream = struct {
434 writeFn: fn(self: &OutStream, bytes: []const u8) -> %void,
435
436 pub fn print(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
437 return std.fmt.format(self, self.writeFn, format, args);
507438 }
508}
509439
510/// `path` may need to be copied in memory to add a null terminating byte. In this case
511/// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
512/// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
513/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
514pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) -> %void {
515 // TODO have an unbuffered File abstraction and use that here.
516 // Then a buffered out stream abstraction can go on top of that for
517 // use cases like stdout and stderr.
518 var out_stream = %return OutStream.open(path, allocator);
519 defer out_stream.close();
520 %return out_stream.write(data);
521 %return out_stream.flush();
522}
440 pub fn write(self: &OutStream, bytes: []const u8) -> %void {
441 return self.writeFn(self, bytes);
442 }
443
444 pub fn writeByte(self: &OutStream, byte: u8) -> %void {
445 const slice = (&byte)[0..1];
446 return self.writeFn(self, slice);
447 }
448};
std/mem.zig-152
......@@ -1,16 +1,9 @@
11const debug = @import("debug.zig");
22const assert = debug.assert;
33const math = @import("math/index.zig");
4const os = @import("os/index.zig");
5const io = @import("io.zig");
6const builtin = @import("builtin");
7const Os = builtin.Os;
8const c = @import("c/index.zig");
94
105pub const Cmp = math.Cmp;
116
12error OutOfMemory;
13
147pub const Allocator = struct {
158 /// Allocate byte_count bytes and return them in a slice, with the
169 /// slicer's pointer aligned at least to alignment bytes.
......@@ -85,151 +78,6 @@ pub const Allocator = struct {
8578 }
8679};
8780
88pub var c_allocator = Allocator {
89 .allocFn = cAlloc,
90 .reallocFn = cRealloc,
91 .freeFn = cFree,
92};
93
94fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
95 if (c.malloc(usize(n))) |mem| {
96 @ptrCast(&u8, mem)[0..n]
97 } else {
98 error.OutOfMemory
99 }
100}
101
102fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
103 if (new_size <= old_mem.len) {
104 old_mem[0..new_size]
105 } else {
106 const old_ptr = @ptrCast(&c_void, old_mem.ptr);
107 if (c.realloc(old_ptr, usize(new_size))) |mem| {
108 @ptrCast(&u8, mem)[0..new_size]
109 } else {
110 error.OutOfMemory
111 }
112 }
113}
114
115fn cFree(self: &Allocator, old_mem: []u8) {
116 const old_ptr = @ptrCast(&c_void, old_mem.ptr);
117 c.free(old_ptr);
118}
119
120pub const IncrementingAllocator = struct {
121 allocator: Allocator,
122 bytes: []u8,
123 end_index: usize,
124 heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
125
126 fn init(capacity: usize) -> %IncrementingAllocator {
127 switch (builtin.os) {
128 Os.linux, Os.darwin, Os.macosx, Os.ios => {
129 const p = os.posix;
130 const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
131 p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
132 if (addr == p.MAP_FAILED) {
133 return error.OutOfMemory;
134 }
135 return IncrementingAllocator {
136 .allocator = Allocator {
137 .allocFn = alloc,
138 .reallocFn = realloc,
139 .freeFn = free,
140 },
141 .bytes = @intToPtr(&u8, addr)[0..capacity],
142 .end_index = 0,
143 .heap_handle = {},
144 };
145 },
146 Os.windows => {
147 const heap_handle = os.windows.GetProcessHeap() ?? return error.OutOfMemory;
148 const ptr = os.windows.HeapAlloc(heap_handle, 0, capacity) ?? return error.OutOfMemory;
149 return IncrementingAllocator {
150 .allocator = Allocator {
151 .allocFn = alloc,
152 .reallocFn = realloc,
153 .freeFn = free,
154 },
155 .bytes = @ptrCast(&u8, ptr)[0..capacity],
156 .end_index = 0,
157 .heap_handle = heap_handle,
158 };
159 },
160 else => @compileError("Unsupported OS"),
161 }
162 }
163
164 fn deinit(self: &IncrementingAllocator) {
165 switch (builtin.os) {
166 Os.linux, Os.darwin, Os.macosx, Os.ios => {
167 _ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
168 },
169 Os.windows => {
170 _ = os.windows.HeapFree(self.heap_handle, 0, @ptrCast(os.windows.LPVOID, self.bytes.ptr));
171 },
172 else => @compileError("Unsupported OS"),
173 }
174 }
175
176 fn reset(self: &IncrementingAllocator) {
177 self.end_index = 0;
178 }
179
180 fn bytesLeft(self: &const IncrementingAllocator) -> usize {
181 return self.bytes.len - self.end_index;
182 }
183
184 fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
185 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
186 const addr = @ptrToInt(&self.bytes[self.end_index]);
187 const rem = @rem(addr, alignment);
188 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
189 const adjusted_index = self.end_index + march_forward_bytes;
190 const new_end_index = adjusted_index + n;
191 if (new_end_index > self.bytes.len) {
192 return error.OutOfMemory;
193 }
194 const result = self.bytes[adjusted_index .. new_end_index];
195 self.end_index = new_end_index;
196 return result;
197 }
198
199 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
200 if (new_size <= old_mem.len) {
201 return old_mem[0..new_size];
202 } else {
203 const result = %return alloc(allocator, new_size, alignment);
204 copy(u8, result, old_mem);
205 return result;
206 }
207 }
208
209 fn free(allocator: &Allocator, bytes: []u8) {
210 // Do nothing. That's the point of an incrementing allocator.
211 }
212};
213
214test "mem.IncrementingAllocator" {
215 const total_bytes = 100 * 1024 * 1024;
216 var inc_allocator = %%IncrementingAllocator.init(total_bytes);
217 defer inc_allocator.deinit();
218
219 const allocator = &inc_allocator.allocator;
220 const slice = %%allocator.alloc(&i32, 100);
221
222 for (slice) |*item, i| {
223 *item = %%allocator.create(i32);
224 **item = i32(i);
225 }
226
227 assert(inc_allocator.bytesLeft() == total_bytes - @sizeOf(i32) * 100 - @sizeOf(usize) * 100);
228
229 inc_allocator.reset();
230
231 assert(inc_allocator.bytesLeft() == total_bytes);
232}
23381
23482/// Copy all of source into dest at position 0.
23583/// dest.len must be >= source.len.
std/os/child_process.zig+39-103
......@@ -28,9 +28,9 @@ pub const ChildProcess = struct {
2828
2929 pub allocator: &mem.Allocator,
3030
31 pub stdin: ?&io.OutStream,
32 pub stdout: ?&io.InStream,
33 pub stderr: ?&io.InStream,
31 pub stdin: ?io.File,
32 pub stdout: ?io.File,
33 pub stderr: ?io.File,
3434
3535 pub term: ?%Term,
3636
......@@ -250,17 +250,17 @@ pub const ChildProcess = struct {
250250 }
251251
252252 fn cleanupStreams(self: &ChildProcess) {
253 if (self.stdin) |stdin| { stdin.close(); self.allocator.destroy(stdin); self.stdin = null; }
254 if (self.stdout) |stdout| { stdout.close(); self.allocator.destroy(stdout); self.stdout = null; }
255 if (self.stderr) |stderr| { stderr.close(); self.allocator.destroy(stderr); self.stderr = null; }
253 if (self.stdin) |*stdin| { stdin.close(); self.stdin = null; }
254 if (self.stdout) |*stdout| { stdout.close(); self.stdout = null; }
255 if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; }
256256 }
257257
258258 fn cleanupAfterWait(self: &ChildProcess, status: i32) -> %Term {
259259 children_nodes.remove(&self.llnode);
260260
261261 defer {
262 os.posixClose(self.err_pipe[0]);
263 os.posixClose(self.err_pipe[1]);
262 os.close(self.err_pipe[0]);
263 os.close(self.err_pipe[1]);
264264 };
265265
266266 // Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
......@@ -310,7 +310,7 @@ pub const ChildProcess = struct {
310310 } else {
311311 undefined
312312 };
313 defer { if (any_ignore) os.posixClose(dev_null_fd); };
313 defer { if (any_ignore) os.close(dev_null_fd); };
314314
315315 var env_map_owned: BufMap = undefined;
316316 var we_own_env_map: bool = undefined;
......@@ -329,27 +329,6 @@ pub const ChildProcess = struct {
329329 const err_pipe = %return makePipe();
330330 %defer destroyPipe(err_pipe);
331331
332 const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) {
333 %return self.allocator.create(io.OutStream)
334 } else {
335 null
336 };
337 %defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr);
338
339 const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
340 %return self.allocator.create(io.InStream)
341 } else {
342 null
343 };
344 %defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr);
345
346 const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
347 %return self.allocator.create(io.InStream)
348 } else {
349 null
350 };
351 %defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr);
352
353332 block_SIGCHLD();
354333 const pid_result = posix.fork();
355334 const pid_err = posix.getErrno(pid_result);
......@@ -390,46 +369,35 @@ pub const ChildProcess = struct {
390369
391370 // we are the parent
392371 const pid = i32(pid_result);
393 if (stdin_ptr) |outstream| {
394 *outstream = io.OutStream {
395 .fd = stdin_pipe[1],
396 .handle = {},
397 .handle_id = {},
398 .buffer = undefined,
399 .index = 0,
400 };
372 if (self.stdin_behavior == StdIo.Pipe) {
373 self.stdin = io.File.openHandle(stdin_pipe[1]);
374 } else {
375 self.stdin = null;
401376 }
402 if (stdout_ptr) |instream| {
403 *instream = io.InStream {
404 .fd = stdout_pipe[0],
405 .handle = {},
406 .handle_id = {},
407 };
377 if (self.stdout_behavior == StdIo.Pipe) {
378 self.stdout = io.File.openHandle(stdout_pipe[0]);
379 } else {
380 self.stdout = null;
408381 }
409 if (stderr_ptr) |instream| {
410 *instream = io.InStream {
411 .fd = stderr_pipe[0],
412 .handle = {},
413 .handle_id = {},
414 };
382 if (self.stderr_behavior == StdIo.Pipe) {
383 self.stderr = io.File.openHandle(stderr_pipe[0]);
384 } else {
385 self.stderr = null;
415386 }
416387
417388 self.pid = pid;
418389 self.err_pipe = err_pipe;
419390 self.llnode = LinkedList(&ChildProcess).Node.init(self);
420391 self.term = null;
421 self.stdin = stdin_ptr;
422 self.stdout = stdout_ptr;
423 self.stderr = stderr_ptr;
424392
425393 // TODO make this atomic so it works even with threads
426394 children_nodes.prepend(&self.llnode);
427395
428396 restore_SIGCHLD();
429397
430 if (self.stdin_behavior == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }
431 if (self.stdout_behavior == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }
432 if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
398 if (self.stdin_behavior == StdIo.Pipe) { os.close(stdin_pipe[0]); }
399 if (self.stdout_behavior == StdIo.Pipe) { os.close(stdout_pipe[1]); }
400 if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
433401 }
434402
435403 fn spawnWindows(self: &ChildProcess) -> %void {
......@@ -509,27 +477,6 @@ pub const ChildProcess = struct {
509477 }
510478 %defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); };
511479
512 const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) {
513 %return self.allocator.create(io.OutStream)
514 } else {
515 null
516 };
517 %defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr);
518
519 const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
520 %return self.allocator.create(io.InStream)
521 } else {
522 null
523 };
524 %defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr);
525
526 const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
527 %return self.allocator.create(io.InStream)
528 } else {
529 null
530 };
531 %defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr);
532
533480 const cmd_line = %return windowsCreateCommandLine(self.allocator, self.argv);
534481 defer self.allocator.free(cmd_line);
535482
......@@ -609,36 +556,25 @@ pub const ChildProcess = struct {
609556 }
610557 };
611558
612 if (stdin_ptr) |outstream| {
613 *outstream = io.OutStream {
614 .fd = {},
615 .handle = g_hChildStd_IN_Wr,
616 .handle_id = undefined,
617 .buffer = undefined,
618 .index = 0,
619 };
559 if (self.stdin_behavior == StdIo.Pipe) {
560 self.stdin = io.File.openHandle(g_hChildStd_IN_Wr);
561 } else {
562 self.stdin = null;
620563 }
621 if (stdout_ptr) |instream| {
622 *instream = io.InStream {
623 .fd = {},
624 .handle = g_hChildStd_OUT_Rd,
625 .handle_id = undefined,
626 };
564 if (self.stdout_behavior == StdIo.Pipe) {
565 self.stdout = io.File.openHandle(g_hChildStd_OUT_Rd);
566 } else {
567 self.stdout = null;
627568 }
628 if (stderr_ptr) |instream| {
629 *instream = io.InStream {
630 .fd = {},
631 .handle = g_hChildStd_ERR_Rd,
632 .handle_id = undefined,
633 };
569 if (self.stderr_behavior == StdIo.Pipe) {
570 self.stderr = io.File.openHandle(g_hChildStd_ERR_Rd);
571 } else {
572 self.stderr = null;
634573 }
635574
636575 self.handle = piProcInfo.hProcess;
637576 self.thread_handle = piProcInfo.hThread;
638577 self.term = null;
639 self.stdin = stdin_ptr;
640 self.stdout = stdout_ptr;
641 self.stderr = stderr_ptr;
642578
643579 if (self.stdin_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_IN_Rd); }
644580 if (self.stderr_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_ERR_Wr); }
......@@ -648,7 +584,7 @@ pub const ChildProcess = struct {
648584 fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
649585 switch (stdio) {
650586 StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno),
651 StdIo.Close => os.posixClose(std_fileno),
587 StdIo.Close => os.close(std_fileno),
652588 StdIo.Inherit => {},
653589 StdIo.Ignore => %return os.posixDup2(dev_null_fd, std_fileno),
654590 }
......@@ -771,8 +707,8 @@ fn makePipe() -> %[2]i32 {
771707}
772708
773709fn destroyPipe(pipe: &const [2]i32) {
774 os.posixClose((*pipe)[0]);
775 os.posixClose((*pipe)[1]);
710 os.close((*pipe)[0]);
711 os.close((*pipe)[1]);
776712}
777713
778714// Child of fork calls this to report an error to the fork parent.
std/os/index.zig+65-22
......@@ -1,6 +1,7 @@
11const builtin = @import("builtin");
22const Os = builtin.Os;
33const is_windows = builtin.os == Os.windows;
4const os = this;
45
56pub const windows = @import("windows/index.zig");
67pub const darwin = @import("darwin.zig");
......@@ -26,14 +27,14 @@ pub const UserInfo = @import("get_user_id.zig").UserInfo;
2627pub const getUserInfo = @import("get_user_id.zig").getUserInfo;
2728
2829const windows_util = @import("windows/util.zig");
29pub const windowsClose = windows_util.windowsClose;
3030pub const windowsWaitSingle = windows_util.windowsWaitSingle;
3131pub const windowsWrite = windows_util.windowsWrite;
32pub const windowsIsTty = windows_util.windowsIsTty;
3332pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
3433pub const windowsOpen = windows_util.windowsOpen;
3534pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
3635
36pub const FileHandle = if (is_windows) windows.HANDLE else i32;
37
3738const debug = @import("../debug.zig");
3839const assert = debug.assert;
3940
......@@ -88,7 +89,7 @@ pub fn getRandomBytes(buf: []u8) -> %void {
8889 Os.darwin, Os.macosx, Os.ios => {
8990 const fd = %return posixOpen("/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC,
9091 0, null);
91 defer posixClose(fd);
92 defer close(fd);
9293
9394 %return posixRead(fd, buf);
9495 },
......@@ -165,14 +166,18 @@ pub coldcc fn exit(status: i32) -> noreturn {
165166 }
166167}
167168
168/// Calls POSIX close, and keeps trying if it gets interrupted.
169pub fn posixClose(fd: i32) {
170 while (true) {
171 const err = posix.getErrno(posix.close(fd));
172 if (err == posix.EINTR) {
173 continue;
174 } else {
175 return;
169/// Closes the file handle. Keeps trying if it gets interrupted by a signal.
170pub fn close(handle: FileHandle) {
171 if (is_windows) {
172 windows_util.windowsClose(handle);
173 } else {
174 while (true) {
175 const err = posix.getErrno(posix.close(handle));
176 if (err == posix.EINTR) {
177 continue;
178 } else {
179 return;
180 }
176181 }
177182 }
178183}
......@@ -716,19 +721,18 @@ pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: [
716721 %return getRandomBytes(rand_buf[0..]);
717722 _ = base64.encodeWithAlphabet(tmp_path[dest_path.len..], rand_buf, b64_fs_alphabet);
718723
719 var out_stream = %return io.OutStream.openMode(tmp_path, mode, allocator);
720 defer out_stream.close();
724 var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
725 defer out_file.close();
721726 %defer _ = deleteFile(allocator, tmp_path);
722727
723 var in_stream = %return io.InStream.open(source_path, allocator);
724 defer in_stream.close();
728 var in_file = %return io.File.openRead(source_path, allocator);
729 defer in_file.close();
725730
726 const buf = out_stream.buffer[0..];
731 var buf: [page_size]u8 = undefined;
727732 while (true) {
728 const amt = %return in_stream.read(buf);
729 out_stream.index = amt;
730 %return out_stream.flush();
731 if (amt != out_stream.buffer.len)
733 const amt = %return in_file.in_stream.read(buf[0..]);
734 %return out_file.out_stream.write(buf[0..amt]);
735 if (amt != buf.len)
732736 return rename(allocator, tmp_path, dest_path);
733737 }
734738}
......@@ -973,7 +977,7 @@ pub const Dir = struct {
973977
974978 pub fn close(self: &Dir) {
975979 self.allocator.free(self.buf);
976 posixClose(self.fd);
980 close(self.fd);
977981 }
978982
979983 /// Memory such as file names referenced in this returned entry becomes invalid
......@@ -1135,7 +1139,6 @@ test "os.sleep" {
11351139 sleep(0, 1);
11361140}
11371141
1138
11391142error ResourceLimitReached;
11401143error InvalidUserId;
11411144error PermissionDenied;
......@@ -1184,6 +1187,21 @@ pub fn posix_setregid(rgid: u32, egid: u32) -> %void {
11841187 };
11851188}
11861189
1190error NoStdHandles;
1191pub fn windowsGetStdHandle(handle_id: windows.DWORD) -> %windows.HANDLE {
1192 if (windows.GetStdHandle(handle_id)) |handle| {
1193 if (handle == windows.INVALID_HANDLE_VALUE) {
1194 const err = windows.GetLastError();
1195 return switch (err) {
1196 else => os.unexpectedErrorWindows(err),
1197 };
1198 }
1199 return handle;
1200 } else {
1201 return error.NoStdHandles;
1202 }
1203}
1204
11871205pub const ArgIteratorPosix = struct {
11881206 index: usize,
11891207 count: usize,
......@@ -1458,3 +1476,28 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) -> error {
14581476 }
14591477 return error.Unexpected;
14601478}
1479
1480pub fn openSelfExe() -> %io.File {
1481 switch (builtin.os) {
1482 Os.linux => {
1483 return io.File.openRead("/proc/self/exe", null);
1484 },
1485 Os.darwin => {
1486 @panic("TODO: openSelfExe on Darwin");
1487 },
1488 else => @compileError("Unsupported OS"),
1489 }
1490}
1491
1492pub fn isTty(handle: FileHandle) -> bool {
1493 if (is_windows) {
1494 return windows_util.windowsIsTty(handle);
1495 } else {
1496 if (builtin.link_libc) {
1497 return c.isatty(handle) != 0;
1498 } else {
1499 return posix.isatty(handle);
1500 }
1501 }
1502}
1503
std/os/path.zig+2-2
......@@ -940,7 +940,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
940940 else => os.unexpectedErrorWindows(err),
941941 };
942942 }
943 defer os.windowsClose(h_file);
943 defer os.close(h_file);
944944 var buf = %return allocator.alloc(u8, 256);
945945 %defer allocator.free(buf);
946946 while (true) {
......@@ -1009,7 +1009,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
10091009 },
10101010 Os.linux => {
10111011 const fd = %return os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator);
1012 defer os.posixClose(fd);
1012 defer os.close(fd);
10131013
10141014 var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
10151015 const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd);
std/special/build_runner.zig+33-27
......@@ -6,6 +6,7 @@ const os = std.os;
66const Builder = std.build.Builder;
77const mem = std.mem;
88const ArrayList = std.ArrayList;
9const warn = std.debug.warn;
910
1011error InvalidArgs;
1112
......@@ -13,7 +14,7 @@ pub fn main() -> %void {
1314 var arg_it = os.args();
1415
1516 // TODO use a more general purpose allocator here
16 var inc_allocator = %%mem.IncrementingAllocator.init(20 * 1024 * 1024);
17 var inc_allocator = %%std.heap.IncrementingAllocator.init(20 * 1024 * 1024);
1718 defer inc_allocator.deinit();
1819
1920 const allocator = &inc_allocator.allocator;
......@@ -23,15 +24,15 @@ pub fn main() -> %void {
2324 _ = arg_it.skip();
2425
2526 const zig_exe = %return unwrapArg(arg_it.next(allocator) ?? {
26 %%io.stderr.printf("Expected first argument to be path to zig compiler\n");
27 warn("Expected first argument to be path to zig compiler\n");
2728 return error.InvalidArgs;
2829 });
2930 const build_root = %return unwrapArg(arg_it.next(allocator) ?? {
30 %%io.stderr.printf("Expected second argument to be build root directory path\n");
31 warn("Expected second argument to be build root directory path\n");
3132 return error.InvalidArgs;
3233 });
3334 const cache_root = %return unwrapArg(arg_it.next(allocator) ?? {
34 %%io.stderr.printf("Expected third argument to be cache root directory path\n");
35 warn("Expected third argument to be cache root directory path\n");
3536 return error.InvalidArgs;
3637 });
3738
......@@ -42,32 +43,37 @@ pub fn main() -> %void {
4243
4344 var prefix: ?[]const u8 = null;
4445
46 var stderr_file = io.getStdErr();
47 var stderr_stream: %&io.OutStream = if (stderr_file) |*f| &f.out_stream else |err| err;
48 var stdout_file = io.getStdOut();
49 var stdout_stream: %&io.OutStream = if (stdout_file) |*f| &f.out_stream else |err| err;
50
4551 while (arg_it.next(allocator)) |err_or_arg| {
4652 const arg = %return unwrapArg(err_or_arg);
4753 if (mem.startsWith(u8, arg, "-D")) {
4854 const option_contents = arg[2..];
4955 if (option_contents.len == 0) {
50 %%io.stderr.printf("Expected option name after '-D'\n\n");
51 return usage(&builder, false, &io.stderr);
56 warn("Expected option name after '-D'\n\n");
57 return usageAndErr(&builder, false, %return stderr_stream);
5258 }
5359 if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
5460 const option_name = option_contents[0..name_end];
5561 const option_value = option_contents[name_end + 1..];
5662 if (builder.addUserInputOption(option_name, option_value))
57 return usage(&builder, false, &io.stderr);
63 return usageAndErr(&builder, false, %return stderr_stream);
5864 } else {
5965 if (builder.addUserInputFlag(option_contents))
60 return usage(&builder, false, &io.stderr);
66 return usageAndErr(&builder, false, %return stderr_stream);
6167 }
6268 } else if (mem.startsWith(u8, arg, "-")) {
6369 if (mem.eql(u8, arg, "--verbose")) {
6470 builder.verbose = true;
6571 } else if (mem.eql(u8, arg, "--help")) {
66 return usage(&builder, false, &io.stdout);
72 return usage(&builder, false, %return stdout_stream);
6773 } else if (mem.eql(u8, arg, "--prefix")) {
6874 prefix = %return unwrapArg(arg_it.next(allocator) ?? {
69 %%io.stderr.printf("Expected argument after --prefix\n\n");
70 return usage(&builder, false, &io.stderr);
75 warn("Expected argument after --prefix\n\n");
76 return usageAndErr(&builder, false, %return stderr_stream);
7177 });
7278 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
7379 builder.verbose_tokenize = true;
......@@ -82,8 +88,8 @@ pub fn main() -> %void {
8288 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
8389 builder.verbose_cimport = true;
8490 } else {
85 %%io.stderr.printf("Unrecognized argument: {}\n\n", arg);
86 return usage(&builder, false, &io.stderr);
91 warn("Unrecognized argument: {}\n\n", arg);
92 return usageAndErr(&builder, false, %return stderr_stream);
8793 }
8894 } else {
8995 %%targets.append(arg);
......@@ -94,11 +100,11 @@ pub fn main() -> %void {
94100 root.build(&builder);
95101
96102 if (builder.validateUserInputDidItFail())
97 return usage(&builder, true, &io.stderr);
103 return usageAndErr(&builder, true, %return stderr_stream);
98104
99105 builder.make(targets.toSliceConst()) %% |err| {
100106 if (err == error.InvalidStepName) {
101 return usage(&builder, true, &io.stderr);
107 return usageAndErr(&builder, true, %return stderr_stream);
102108 }
103109 return err;
104110 };
......@@ -112,7 +118,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
112118 }
113119
114120 // This usage text has to be synchronized with src/main.cpp
115 %%out_stream.printf(
121 %return out_stream.print(
116122 \\Usage: {} build [steps] [options]
117123 \\
118124 \\Steps:
......@@ -121,10 +127,10 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
121127
122128 const allocator = builder.allocator;
123129 for (builder.top_level_steps.toSliceConst()) |top_level_step| {
124 %%out_stream.printf(" {s22} {}\n", top_level_step.step.name, top_level_step.description);
130 %return out_stream.print(" {s22} {}\n", top_level_step.step.name, top_level_step.description);
125131 }
126132
127 %%out_stream.write(
133 %return out_stream.write(
128134 \\
129135 \\General Options:
130136 \\ --help Print this help and exit
......@@ -136,17 +142,17 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
136142 );
137143
138144 if (builder.available_options_list.len == 0) {
139 %%out_stream.print(" (none)\n");
145 %return out_stream.print(" (none)\n");
140146 } else {
141147 for (builder.available_options_list.toSliceConst()) |option| {
142 const name = %%fmt.allocPrint(allocator,
148 const name = %return fmt.allocPrint(allocator,
143149 " -D{}=${}", option.name, Builder.typeIdName(option.type_id));
144150 defer allocator.free(name);
145 %%out_stream.print("{s24} {}\n", name, option.description);
151 %return out_stream.print("{s24} {}\n", name, option.description);
146152 }
147153 }
148154
149 %%out_stream.write(
155 %return out_stream.write(
150156 \\
151157 \\Advanced Options:
152158 \\ --build-file $file Override path to build.zig
......@@ -159,16 +165,16 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
159165 \\ --verbose-cimport Enable compiler debug output for C imports
160166 \\
161167 );
168}
162169
163 %%out_stream.flush();
164
165 if (out_stream == &io.stderr)
166 return error.InvalidArgs;
170fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) -> error {
171 usage(builder, already_ran_build, out_stream) %% {};
172 return error.InvalidArgs;
167173}
168174
169175fn unwrapArg(arg: %[]u8) -> %[]u8 {
170176 return arg %% |err| {
171 %%io.stderr.printf("Unable to parse command line: {}\n", err);
177 warn("Unable to parse command line: {}\n", err);
172178 return err;
173179 };
174180}
std/special/test_runner.zig+5-3
......@@ -1,13 +1,15 @@
1const io = @import("std").io;
1const std = @import("std");
2const io = std.io;
23const builtin = @import("builtin");
34const test_fn_list = builtin.__zig_test_fn_slice;
5const warn = std.debug.warn;
46
57pub fn main() -> %void {
68 for (test_fn_list) |test_fn, i| {
7 %%io.stderr.printf("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
9 warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
810
911 test_fn.func();
1012
11 %%io.stderr.printf("OK\n");
13 warn("OK\n");
1214 }
1315}
test/compare_output.zig+44-31
......@@ -17,7 +17,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
1717 \\
1818 \\pub fn main() -> %void {
1919 \\ privateFunction();
20 \\ %%stdout.printf("OK 2\n");
20 \\ const stdout = &(%%getStdOut()).out_stream;
21 \\ %%stdout.print("OK 2\n");
2122 \\}
2223 \\
2324 \\fn privateFunction() {
......@@ -31,7 +32,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
3132 \\// purposefully conflicting function with main.zig
3233 \\// but it's private so it should be OK
3334 \\fn privateFunction() {
34 \\ %%stdout.printf("OK 1\n");
35 \\ const stdout = &(%%getStdOut()).out_stream;
36 \\ %%stdout.print("OK 1\n");
3537 \\}
3638 \\
3739 \\pub fn printText() {
......@@ -56,7 +58,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
5658 tc.addSourceFile("foo.zig",
5759 \\use @import("std").io;
5860 \\pub fn foo_function() {
59 \\ %%stdout.printf("OK\n");
61 \\ const stdout = &(%%getStdOut()).out_stream;
62 \\ %%stdout.print("OK\n");
6063 \\}
6164 );
6265
......@@ -66,7 +69,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
6669 \\
6770 \\pub fn bar_function() {
6871 \\ if (foo_function()) {
69 \\ %%stdout.printf("OK\n");
72 \\ const stdout = &(%%getStdOut()).out_stream;
73 \\ %%stdout.print("OK\n");
7074 \\ }
7175 \\}
7276 );
......@@ -97,7 +101,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
97101 \\pub const a_text = "OK\n";
98102 \\
99103 \\pub fn ok() {
100 \\ %%io.stdout.printf(b_text);
104 \\ const stdout = &(%%io.getStdOut()).out_stream;
105 \\ %%stdout.print(b_text);
101106 \\}
102107 );
103108
......@@ -114,7 +119,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
114119 \\const io = @import("std").io;
115120 \\
116121 \\pub fn main() -> %void {
117 \\ %%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
122 \\ const stdout = &(%%io.getStdOut()).out_stream;
123 \\ %%stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
118124 \\}
119125 , "Hello, world!\n0012 012 a\n");
120126
......@@ -266,7 +272,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
266272 \\ var x_local : i32 = print_ok(x);
267273 \\}
268274 \\fn print_ok(val: @typeOf(x)) -> @typeOf(foo) {
269 \\ %%io.stdout.printf("OK\n");
275 \\ const stdout = &(%%io.getStdOut()).out_stream;
276 \\ %%stdout.print("OK\n");
270277 \\ return 0;
271278 \\}
272279 \\const foo : i32 = 0;
......@@ -347,24 +354,26 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
347354 \\pub fn main() -> %void {
348355 \\ const bar = Bar {.field2 = 13,};
349356 \\ const foo = Foo {.field1 = bar,};
357 \\ const stdout = &(%%io.getStdOut()).out_stream;
350358 \\ if (!foo.method()) {
351 \\ %%io.stdout.printf("BAD\n");
359 \\ %%stdout.print("BAD\n");
352360 \\ }
353361 \\ if (!bar.method()) {
354 \\ %%io.stdout.printf("BAD\n");
362 \\ %%stdout.print("BAD\n");
355363 \\ }
356 \\ %%io.stdout.printf("OK\n");
364 \\ %%stdout.print("OK\n");
357365 \\}
358366 , "OK\n");
359367
360368 cases.add("defer with only fallthrough",
361369 \\const io = @import("std").io;
362370 \\pub fn main() -> %void {
363 \\ %%io.stdout.printf("before\n");
364 \\ defer %%io.stdout.printf("defer1\n");
365 \\ defer %%io.stdout.printf("defer2\n");
366 \\ defer %%io.stdout.printf("defer3\n");
367 \\ %%io.stdout.printf("after\n");
371 \\ const stdout = &(%%io.getStdOut()).out_stream;
372 \\ %%stdout.print("before\n");
373 \\ defer %%stdout.print("defer1\n");
374 \\ defer %%stdout.print("defer2\n");
375 \\ defer %%stdout.print("defer3\n");
376 \\ %%stdout.print("after\n");
368377 \\}
369378 , "before\nafter\ndefer3\ndefer2\ndefer1\n");
370379
......@@ -372,13 +381,14 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
372381 \\const io = @import("std").io;
373382 \\const os = @import("std").os;
374383 \\pub fn main() -> %void {
375 \\ %%io.stdout.printf("before\n");
376 \\ defer %%io.stdout.printf("defer1\n");
377 \\ defer %%io.stdout.printf("defer2\n");
384 \\ const stdout = &(%%io.getStdOut()).out_stream;
385 \\ %%stdout.print("before\n");
386 \\ defer %%stdout.print("defer1\n");
387 \\ defer %%stdout.print("defer2\n");
378388 \\ var args_it = @import("std").os.args();
379389 \\ if (args_it.skip() and !args_it.skip()) return;
380 \\ defer %%io.stdout.printf("defer3\n");
381 \\ %%io.stdout.printf("after\n");
390 \\ defer %%stdout.print("defer3\n");
391 \\ %%stdout.print("after\n");
382392 \\}
383393 , "before\ndefer2\ndefer1\n");
384394
......@@ -388,12 +398,13 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
388398 \\ do_test() %% return;
389399 \\}
390400 \\fn do_test() -> %void {
391 \\ %%io.stdout.printf("before\n");
392 \\ defer %%io.stdout.printf("defer1\n");
393 \\ %defer %%io.stdout.printf("deferErr\n");
401 \\ const stdout = &(%%io.getStdOut()).out_stream;
402 \\ %%stdout.print("before\n");
403 \\ defer %%stdout.print("defer1\n");
404 \\ %defer %%stdout.print("deferErr\n");
394405 \\ %return its_gonna_fail();
395 \\ defer %%io.stdout.printf("defer3\n");
396 \\ %%io.stdout.printf("after\n");
406 \\ defer %%stdout.print("defer3\n");
407 \\ %%stdout.print("after\n");
397408 \\}
398409 \\error IToldYouItWouldFail;
399410 \\fn its_gonna_fail() -> %void {
......@@ -407,12 +418,13 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
407418 \\ do_test() %% return;
408419 \\}
409420 \\fn do_test() -> %void {
410 \\ %%io.stdout.printf("before\n");
411 \\ defer %%io.stdout.printf("defer1\n");
412 \\ %defer %%io.stdout.printf("deferErr\n");
421 \\ const stdout = &(%%io.getStdOut()).out_stream;
422 \\ %%stdout.print("before\n");
423 \\ defer %%stdout.print("defer1\n");
424 \\ %defer %%stdout.print("deferErr\n");
413425 \\ %return its_gonna_pass();
414 \\ defer %%io.stdout.printf("defer3\n");
415 \\ %%io.stdout.printf("after\n");
426 \\ defer %%stdout.print("defer3\n");
427 \\ %%stdout.print("after\n");
416428 \\}
417429 \\fn its_gonna_pass() -> %void { }
418430 , "before\nafter\ndefer3\ndefer1\n");
......@@ -423,7 +435,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
423435 \\const io = @import("std").io;
424436 \\
425437 \\pub fn main() -> %void {
426 \\ %%io.stdout.printf(foo_txt);
438 \\ const stdout = &(%%io.getStdOut()).out_stream;
439 \\ %%stdout.print(foo_txt);
427440 \\}
428441 , "1234\nabcd\n");
429442
test/tests.zig+36-33
......@@ -1,5 +1,6 @@
11const std = @import("std");
22const debug = std.debug;
3const warn = debug.warn;
34const build = std.build;
45const os = std.os;
56const StdIo = os.ChildProcess.StdIo;
......@@ -50,6 +51,8 @@ const test_targets = []TestTarget {
5051
5152error TestFailed;
5253
54const max_stdout_size = 1 * 1024 * 1024; // 1 MB
55
5356pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
5457 const cases = %%b.allocator.create(CompareOutputContext);
5558 *cases = CompareOutputContext {
......@@ -231,7 +234,7 @@ pub const CompareOutputContext = struct {
231234
232235 const full_exe_path = b.pathFromRoot(self.exe_path);
233236
234 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
237 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
235238
236239 const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
237240 defer child.deinit();
......@@ -246,8 +249,8 @@ pub const CompareOutputContext = struct {
246249 var stdout = Buffer.initNull(b.allocator);
247250 var stderr = Buffer.initNull(b.allocator);
248251
249 %%(??child.stdout).readAll(&stdout);
250 %%(??child.stderr).readAll(&stderr);
252 %%(??child.stdout).in_stream.readAllBuffer(&stdout, max_stdout_size);
253 %%(??child.stderr).in_stream.readAllBuffer(&stderr, max_stdout_size);
251254
252255 const term = child.wait() %% |err| {
253256 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
......@@ -255,19 +258,19 @@ pub const CompareOutputContext = struct {
255258 switch (term) {
256259 Term.Exited => |code| {
257260 if (code != 0) {
258 %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
261 warn("Process {} exited with error code {}\n", full_exe_path, code);
259262 return error.TestFailed;
260263 }
261264 },
262265 else => {
263 %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
266 warn("Process {} terminated unexpectedly\n", full_exe_path);
264267 return error.TestFailed;
265268 },
266269 };
267270
268271
269272 if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
270 %%io.stderr.printf(
273 warn(
271274 \\
272275 \\========= Expected this output: =========
273276 \\{}
......@@ -277,7 +280,7 @@ pub const CompareOutputContext = struct {
277280 , self.expected_output, stdout.toSliceConst());
278281 return error.TestFailed;
279282 }
280 %%io.stderr.printf("OK\n");
283 warn("OK\n");
281284 }
282285 };
283286
......@@ -310,7 +313,7 @@ pub const CompareOutputContext = struct {
310313
311314 const full_exe_path = b.pathFromRoot(self.exe_path);
312315
313 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
316 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
314317
315318 const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
316319 defer child.deinit();
......@@ -328,24 +331,24 @@ pub const CompareOutputContext = struct {
328331 switch (term) {
329332 Term.Exited => |code| {
330333 if (code != expected_exit_code) {
331 %%io.stderr.printf("\nProgram expected to exit with code {} " ++
334 warn("\nProgram expected to exit with code {} " ++
332335 "but exited with code {}\n", expected_exit_code, code);
333336 return error.TestFailed;
334337 }
335338 },
336339 Term.Signal => |sig| {
337 %%io.stderr.printf("\nProgram expected to exit with code {} " ++
340 warn("\nProgram expected to exit with code {} " ++
338341 "but instead signaled {}\n", expected_exit_code, sig);
339342 return error.TestFailed;
340343 },
341344 else => {
342 %%io.stderr.printf("\nProgram expected to exit with code {}" ++
345 warn("\nProgram expected to exit with code {}" ++
343346 " but exited in an unexpected way\n", expected_exit_code);
344347 return error.TestFailed;
345348 },
346349 }
347350
348 %%io.stderr.printf("OK\n");
351 warn("OK\n");
349352 }
350353 };
351354
......@@ -554,7 +557,7 @@ pub const CompileErrorContext = struct {
554557 Mode.ReleaseFast => %%zig_args.append("--release-fast"),
555558 }
556559
557 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
560 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
558561
559562 if (b.verbose) {
560563 printInvocation(zig_args.toSliceConst());
......@@ -573,8 +576,8 @@ pub const CompileErrorContext = struct {
573576 var stdout_buf = Buffer.initNull(b.allocator);
574577 var stderr_buf = Buffer.initNull(b.allocator);
575578
576 %%(??child.stdout).readAll(&stdout_buf);
577 %%(??child.stderr).readAll(&stderr_buf);
579 %%(??child.stdout).in_stream.readAllBuffer(&stdout_buf, max_stdout_size);
580 %%(??child.stderr).in_stream.readAllBuffer(&stderr_buf, max_stdout_size);
578581
579582 const term = child.wait() %% |err| {
580583 debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err));
......@@ -582,12 +585,12 @@ pub const CompileErrorContext = struct {
582585 switch (term) {
583586 Term.Exited => |code| {
584587 if (code == 0) {
585 %%io.stderr.printf("Compilation incorrectly succeeded\n");
588 warn("Compilation incorrectly succeeded\n");
586589 return error.TestFailed;
587590 }
588591 },
589592 else => {
590 %%io.stderr.printf("Process {} terminated unexpectedly\n", b.zig_exe);
593 warn("Process {} terminated unexpectedly\n", b.zig_exe);
591594 return error.TestFailed;
592595 },
593596 };
......@@ -597,7 +600,7 @@ pub const CompileErrorContext = struct {
597600 const stderr = stderr_buf.toSliceConst();
598601
599602 if (stdout.len != 0) {
600 %%io.stderr.printf(
603 warn(
601604 \\
602605 \\Expected empty stdout, instead found:
603606 \\================================================
......@@ -610,7 +613,7 @@ pub const CompileErrorContext = struct {
610613
611614 for (self.case.expected_errors.toSliceConst()) |expected_error| {
612615 if (mem.indexOf(u8, stderr, expected_error) == null) {
613 %%io.stderr.printf(
616 warn(
614617 \\
615618 \\========= Expected this compile error: =========
616619 \\{}
......@@ -621,15 +624,15 @@ pub const CompileErrorContext = struct {
621624 return error.TestFailed;
622625 }
623626 }
624 %%io.stderr.printf("OK\n");
627 warn("OK\n");
625628 }
626629 };
627630
628631 fn printInvocation(args: []const []const u8) {
629632 for (args) |arg| {
630 %%io.stderr.printf("{} ", arg);
633 warn("{} ", arg);
631634 }
632 %%io.stderr.printf("\n");
635 warn("\n");
633636 }
634637
635638 pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8,
......@@ -822,7 +825,7 @@ pub const ParseCContext = struct {
822825 %%zig_args.append("parsec");
823826 %%zig_args.append(b.pathFromRoot(root_src));
824827
825 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
828 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
826829
827830 if (b.verbose) {
828831 printInvocation(zig_args.toSliceConst());
......@@ -841,8 +844,8 @@ pub const ParseCContext = struct {
841844 var stdout_buf = Buffer.initNull(b.allocator);
842845 var stderr_buf = Buffer.initNull(b.allocator);
843846
844 %%(??child.stdout).readAll(&stdout_buf);
845 %%(??child.stderr).readAll(&stderr_buf);
847 %%(??child.stdout).in_stream.readAllBuffer(&stdout_buf, max_stdout_size);
848 %%(??child.stderr).in_stream.readAllBuffer(&stderr_buf, max_stdout_size);
846849
847850 const term = child.wait() %% |err| {
848851 debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err));
......@@ -850,16 +853,16 @@ pub const ParseCContext = struct {
850853 switch (term) {
851854 Term.Exited => |code| {
852855 if (code != 0) {
853 %%io.stderr.printf("Compilation failed with exit code {}\n", code);
856 warn("Compilation failed with exit code {}\n", code);
854857 return error.TestFailed;
855858 }
856859 },
857860 Term.Signal => |code| {
858 %%io.stderr.printf("Compilation failed with signal {}\n", code);
861 warn("Compilation failed with signal {}\n", code);
859862 return error.TestFailed;
860863 },
861864 else => {
862 %%io.stderr.printf("Compilation terminated unexpectedly\n");
865 warn("Compilation terminated unexpectedly\n");
863866 return error.TestFailed;
864867 },
865868 };
......@@ -868,7 +871,7 @@ pub const ParseCContext = struct {
868871 const stderr = stderr_buf.toSliceConst();
869872
870873 if (stderr.len != 0 and !self.case.allow_warnings) {
871 %%io.stderr.printf(
874 warn(
872875 \\====== parsec emitted warnings: ============
873876 \\{}
874877 \\============================================
......@@ -879,7 +882,7 @@ pub const ParseCContext = struct {
879882
880883 for (self.case.expected_lines.toSliceConst()) |expected_line| {
881884 if (mem.indexOf(u8, stdout, expected_line) == null) {
882 %%io.stderr.printf(
885 warn(
883886 \\
884887 \\========= Expected this output: ================
885888 \\{}
......@@ -890,15 +893,15 @@ pub const ParseCContext = struct {
890893 return error.TestFailed;
891894 }
892895 }
893 %%io.stderr.printf("OK\n");
896 warn("OK\n");
894897 }
895898 };
896899
897900 fn printInvocation(args: []const []const u8) {
898901 for (args) |arg| {
899 %%io.stderr.printf("{} ", arg);
902 warn("{} ", arg);
900903 }
901 %%io.stderr.printf("\n");
904 warn("\n");
902905 }
903906
904907 pub fn create(self: &ParseCContext, allow_warnings: bool, filename: []const u8, name: []const u8,