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.master
parent
7a96aca39e
commit
9e234d4208
|
@ -521,6 +521,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/fmt/errol/index.zig" DESTINATION "${ZIG_S
|
|||
install(FILES "${CMAKE_SOURCE_DIR}/std/fmt/errol/lookup.zig" DESTINATION "${ZIG_STD_DEST}/fmt/errol")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/fmt/index.zig" DESTINATION "${ZIG_STD_DEST}/fmt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/heap.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}")
|
||||
install(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
|
|||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/fixunstfsi.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/fixunstfti.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/index.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivti3.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivmod.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivmoddi4.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivmodti4.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/udivti3.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt/umodti3.zig" DESTINATION "${ZIG_STD_DEST}/special/compiler_rt")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/panic.zig" DESTINATION "${ZIG_STD_DEST}/special")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/std/special/test_runner.zig" DESTINATION "${ZIG_STD_DEST}/special")
|
||||
|
|
|
@ -2,47 +2,52 @@ const std = @import("std");
|
|||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const os = std.os;
|
||||
const warn = std.debug.warn;
|
||||
|
||||
pub fn main() -> %void {
|
||||
const allocator = &std.debug.global_allocator;
|
||||
var args_it = os.args();
|
||||
const exe = %return unwrapArg(??args_it.next(allocator));
|
||||
var catted_anything = false;
|
||||
var stdout_file = %return io.getStdOut();
|
||||
const stdout = &stdout_file.out_stream;
|
||||
|
||||
while (args_it.next(allocator)) |arg_or_err| {
|
||||
const arg = %return unwrapArg(arg_or_err);
|
||||
if (mem.eql(u8, arg, "-")) {
|
||||
catted_anything = true;
|
||||
%return cat_stream(&io.stdin);
|
||||
var stdin_file = %return io.getStdIn();
|
||||
%return cat_stream(stdout, &stdin_file.in_stream);
|
||||
} else if (arg[0] == '-') {
|
||||
return usage(exe);
|
||||
} else {
|
||||
var is = io.InStream.open(arg, null) %% |err| {
|
||||
%%io.stderr.printf("Unable to open file: {}\n", @errorName(err));
|
||||
var file = io.File.openRead(arg, null) %% |err| {
|
||||
warn("Unable to open file: {}\n", @errorName(err));
|
||||
return err;
|
||||
};
|
||||
defer is.close();
|
||||
defer file.close();
|
||||
|
||||
catted_anything = true;
|
||||
%return cat_stream(&is);
|
||||
%return cat_stream(stdout, &file.in_stream);
|
||||
}
|
||||
}
|
||||
if (!catted_anything) {
|
||||
%return cat_stream(&io.stdin);
|
||||
var stdin_file = %return io.getStdIn();
|
||||
%return cat_stream(stdout, &stdin_file.in_stream);
|
||||
}
|
||||
%return io.stdout.flush();
|
||||
}
|
||||
|
||||
fn usage(exe: []const u8) -> %void {
|
||||
%%io.stderr.printf("Usage: {} [FILE]...\n", exe);
|
||||
warn("Usage: {} [FILE]...\n", exe);
|
||||
return error.Invalid;
|
||||
}
|
||||
|
||||
fn cat_stream(is: &io.InStream) -> %void {
|
||||
fn cat_stream(stdout: &io.OutStream, is: &io.InStream) -> %void {
|
||||
var buf: [1024 * 4]u8 = undefined;
|
||||
|
||||
while (true) {
|
||||
const bytes_read = is.read(buf[0..]) %% |err| {
|
||||
%%io.stderr.printf("Unable to read from stream: {}\n", @errorName(err));
|
||||
warn("Unable to read from stream: {}\n", @errorName(err));
|
||||
return err;
|
||||
};
|
||||
|
||||
|
@ -50,8 +55,8 @@ fn cat_stream(is: &io.InStream) -> %void {
|
|||
break;
|
||||
}
|
||||
|
||||
io.stdout.write(buf[0..bytes_read]) %% |err| {
|
||||
%%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err));
|
||||
stdout.write(buf[0..bytes_read]) %% |err| {
|
||||
warn("Unable to write to stdout: {}\n", @errorName(err));
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
@ -59,7 +64,7 @@ fn cat_stream(is: &io.InStream) -> %void {
|
|||
|
||||
fn unwrapArg(arg: %[]u8) -> %[]u8 {
|
||||
return arg %% |err| {
|
||||
%%io.stderr.printf("Unable to parse command line: {}\n", err);
|
||||
warn("Unable to parse command line: {}\n", err);
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,7 +5,13 @@ const Rand = std.rand.Rand;
|
|||
const os = std.os;
|
||||
|
||||
pub fn main() -> %void {
|
||||
%%io.stdout.printf("Welcome to the Guess Number Game in Zig.\n");
|
||||
var stdout_file = %return io.getStdOut();
|
||||
const stdout = &stdout_file.out_stream;
|
||||
|
||||
var stdin_file = %return io.getStdIn();
|
||||
const stdin = &stdin_file.in_stream;
|
||||
|
||||
%return stdout.print("Welcome to the Guess Number Game in Zig.\n");
|
||||
|
||||
var seed_bytes: [@sizeOf(usize)]u8 = undefined;
|
||||
%%os.getRandomBytes(seed_bytes[0..]);
|
||||
|
@ -15,24 +21,24 @@ pub fn main() -> %void {
|
|||
const answer = rand.range(u8, 0, 100) + 1;
|
||||
|
||||
while (true) {
|
||||
%%io.stdout.printf("\nGuess a number between 1 and 100: ");
|
||||
%return stdout.print("\nGuess a number between 1 and 100: ");
|
||||
var line_buf : [20]u8 = undefined;
|
||||
|
||||
const line_len = io.stdin.read(line_buf[0..]) %% |err| {
|
||||
%%io.stdout.printf("Unable to read from stdin: {}\n", @errorName(err));
|
||||
const line_len = stdin.read(line_buf[0..]) %% |err| {
|
||||
%return stdout.print("Unable to read from stdin: {}\n", @errorName(err));
|
||||
return err;
|
||||
};
|
||||
|
||||
const guess = fmt.parseUnsigned(u8, line_buf[0..line_len - 1], 10) %% {
|
||||
%%io.stdout.printf("Invalid number.\n");
|
||||
%return stdout.print("Invalid number.\n");
|
||||
continue;
|
||||
};
|
||||
if (guess > answer) {
|
||||
%%io.stdout.printf("Guess lower.\n");
|
||||
%return stdout.print("Guess lower.\n");
|
||||
} else if (guess < answer) {
|
||||
%%io.stdout.printf("Guess higher.\n");
|
||||
%return stdout.print("Guess higher.\n");
|
||||
} else {
|
||||
%%io.stdout.printf("You win!\n");
|
||||
%return stdout.print("You win!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
const io = @import("std").io;
|
||||
const std = @import("std");
|
||||
|
||||
pub fn main() -> %void {
|
||||
%return io.stdout.printf("Hello, world!\n");
|
||||
// If this program is run without stdout attached, exit with an error.
|
||||
var stdout_file = %return std.io.getStdOut();
|
||||
const stdout = &stdout_file.out_stream;
|
||||
// If this program encounters pipe failure when printing to stdout, exit
|
||||
// with an error.
|
||||
%return stdout.print("Hello, world!\n");
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ pub fn ArrayList(comptime T: type) -> type{
|
|||
l.len = new_len;
|
||||
}
|
||||
|
||||
pub fn resizeDown(l: &Self, new_len: usize) {
|
||||
pub fn shrink(l: &Self, new_len: usize) {
|
||||
assert(new_len <= l.len);
|
||||
l.len = new_len;
|
||||
}
|
||||
|
|
|
@ -71,6 +71,12 @@ pub const Buffer = struct {
|
|||
return self.list.toSliceConst()[0..self.len()];
|
||||
}
|
||||
|
||||
pub fn shrink(self: &Buffer, new_len: usize) {
|
||||
assert(new_len <= self.len());
|
||||
self.list.shrink(new_len + 1);
|
||||
self.list.items[self.len()] = 0;
|
||||
}
|
||||
|
||||
pub fn resize(self: &Buffer, new_len: usize) -> %void {
|
||||
%return self.list.resize(new_len + 1);
|
||||
self.list.items[self.len()] = 0;
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
const std = @import("index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const io = @import("io.zig");
|
||||
const mem = @import("mem.zig");
|
||||
const debug = @import("debug.zig");
|
||||
const io = std.io;
|
||||
const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
const ArrayList = @import("array_list.zig").ArrayList;
|
||||
const HashMap = @import("hash_map.zig").HashMap;
|
||||
const Allocator = @import("mem.zig").Allocator;
|
||||
const os = @import("os/index.zig");
|
||||
const warn = std.debug.warn;
|
||||
const ArrayList = std.ArrayList;
|
||||
const HashMap = std.HashMap;
|
||||
const Allocator = mem.Allocator;
|
||||
const os = std.os;
|
||||
const StdIo = os.ChildProcess.StdIo;
|
||||
const Term = os.ChildProcess.Term;
|
||||
const BufSet = @import("buf_set.zig").BufSet;
|
||||
const BufMap = @import("buf_map.zig").BufMap;
|
||||
const fmt_lib = @import("fmt/index.zig");
|
||||
const BufSet = std.BufSet;
|
||||
const BufMap = std.BufMap;
|
||||
const fmt_lib = std.fmt;
|
||||
|
||||
error ExtraArg;
|
||||
error UncleanExit;
|
||||
|
@ -280,7 +282,7 @@ pub const Builder = struct {
|
|||
|
||||
for (self.installed_files.toSliceConst()) |installed_file| {
|
||||
if (self.verbose) {
|
||||
%%io.stderr.printf("rm {}\n", installed_file);
|
||||
warn("rm {}\n", installed_file);
|
||||
}
|
||||
_ = os.deleteFile(self.allocator, installed_file);
|
||||
}
|
||||
|
@ -290,7 +292,7 @@ pub const Builder = struct {
|
|||
|
||||
fn makeOneStep(self: &Builder, s: &Step) -> %void {
|
||||
if (s.loop_flag) {
|
||||
%%io.stderr.printf("Dependency loop detected:\n {}\n", s.name);
|
||||
warn("Dependency loop detected:\n {}\n", s.name);
|
||||
return error.DependencyLoopDetected;
|
||||
}
|
||||
s.loop_flag = true;
|
||||
|
@ -298,7 +300,7 @@ pub const Builder = struct {
|
|||
for (s.dependencies.toSlice()) |dep| {
|
||||
self.makeOneStep(dep) %% |err| {
|
||||
if (err == error.DependencyLoopDetected) {
|
||||
%%io.stderr.printf(" {}\n", s.name);
|
||||
warn(" {}\n", s.name);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
|
@ -315,7 +317,7 @@ pub const Builder = struct {
|
|||
return &top_level_step.step;
|
||||
}
|
||||
}
|
||||
%%io.stderr.printf("Cannot run step '{}' because it does not exist\n", name);
|
||||
warn("Cannot run step '{}' because it does not exist\n", name);
|
||||
return error.InvalidStepName;
|
||||
}
|
||||
|
||||
|
@ -326,12 +328,12 @@ pub const Builder = struct {
|
|||
const word = it.next() ?? break;
|
||||
if (mem.eql(u8, word, "-isystem")) {
|
||||
const include_path = it.next() ?? {
|
||||
%%io.stderr.printf("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");
|
||||
warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");
|
||||
break;
|
||||
};
|
||||
self.addCIncludePath(include_path);
|
||||
} else {
|
||||
%%io.stderr.printf("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}\n", word);
|
||||
warn("Unrecognized C flag from NIX_CFLAGS_COMPILE: {}\n", word);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -344,7 +346,7 @@ pub const Builder = struct {
|
|||
const word = it.next() ?? break;
|
||||
if (mem.eql(u8, word, "-rpath")) {
|
||||
const rpath = it.next() ?? {
|
||||
%%io.stderr.printf("Expected argument after -rpath in NIX_LDFLAGS\n");
|
||||
warn("Expected argument after -rpath in NIX_LDFLAGS\n");
|
||||
break;
|
||||
};
|
||||
self.addRPath(rpath);
|
||||
|
@ -352,7 +354,7 @@ pub const Builder = struct {
|
|||
const lib_path = word[2..];
|
||||
self.addLibPath(lib_path);
|
||||
} else {
|
||||
%%io.stderr.printf("Unrecognized C flag from NIX_LDFLAGS: {}\n", word);
|
||||
warn("Unrecognized C flag from NIX_LDFLAGS: {}\n", word);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -384,13 +386,13 @@ pub const Builder = struct {
|
|||
} else if (mem.eql(u8, s, "false")) {
|
||||
return false;
|
||||
} else {
|
||||
%%io.stderr.printf("Expected -D{} to be a boolean, but received '{}'\n", name, s);
|
||||
warn("Expected -D{} to be a boolean, but received '{}'\n", name, s);
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
}
|
||||
},
|
||||
UserValue.List => {
|
||||
%%io.stderr.printf("Expected -D{} to be a boolean, but received a list.\n", name);
|
||||
warn("Expected -D{} to be a boolean, but received a list.\n", name);
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
|
@ -399,12 +401,12 @@ pub const Builder = struct {
|
|||
TypeId.Float => debug.panic("TODO float options to build script"),
|
||||
TypeId.String => switch (entry.value.value) {
|
||||
UserValue.Flag => {
|
||||
%%io.stderr.printf("Expected -D{} to be a string, but received a boolean.\n", name);
|
||||
warn("Expected -D{} to be a string, but received a boolean.\n", name);
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
UserValue.List => {
|
||||
%%io.stderr.printf("Expected -D{} to be a string, but received a list.\n", name);
|
||||
warn("Expected -D{} to be a string, but received a list.\n", name);
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
|
@ -437,7 +439,7 @@ pub const Builder = struct {
|
|||
} else if (!release_fast and !release_safe) {
|
||||
builtin.Mode.Debug
|
||||
} else {
|
||||
%%io.stderr.printf("Both -Drelease-safe and -Drelease-fast specified");
|
||||
warn("Both -Drelease-safe and -Drelease-fast specified");
|
||||
self.markInvalidUserInput();
|
||||
builtin.Mode.Debug
|
||||
};
|
||||
|
@ -474,7 +476,7 @@ pub const Builder = struct {
|
|||
});
|
||||
},
|
||||
UserValue.Flag => {
|
||||
%%io.stderr.printf("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
|
||||
warn("Option '-D{}={}' conflicts with flag '-D{}'.\n", name, value, name);
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
@ -490,11 +492,11 @@ pub const Builder = struct {
|
|||
})) |*prev_value| {
|
||||
switch (prev_value.value) {
|
||||
UserValue.Scalar => |s| {
|
||||
%%io.stderr.printf("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
|
||||
warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", name, name, s);
|
||||
return true;
|
||||
},
|
||||
UserValue.List => {
|
||||
%%io.stderr.printf("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
|
||||
warn("Flag '-D{}' conflicts with multiple options of the same name.\n", name);
|
||||
return true;
|
||||
},
|
||||
UserValue.Flag => {},
|
||||
|
@ -536,7 +538,7 @@ pub const Builder = struct {
|
|||
while (true) {
|
||||
const entry = it.next() ?? break;
|
||||
if (!entry.value.used) {
|
||||
%%io.stderr.printf("Invalid option: -D{}\n\n", entry.key);
|
||||
warn("Invalid option: -D{}\n\n", entry.key);
|
||||
self.markInvalidUserInput();
|
||||
}
|
||||
}
|
||||
|
@ -549,11 +551,11 @@ pub const Builder = struct {
|
|||
}
|
||||
|
||||
fn printCmd(cwd: ?[]const u8, argv: []const []const u8) {
|
||||
if (cwd) |yes_cwd| %%io.stderr.print("cd {} && ", yes_cwd);
|
||||
if (cwd) |yes_cwd| warn("cd {} && ", yes_cwd);
|
||||
for (argv) |arg| {
|
||||
%%io.stderr.print("{} ", arg);
|
||||
warn("{} ", arg);
|
||||
}
|
||||
%%io.stderr.printf("\n");
|
||||
warn("\n");
|
||||
}
|
||||
|
||||
fn spawnChildEnvMap(self: &Builder, cwd: ?[]const u8, env_map: &const BufMap,
|
||||
|
@ -570,20 +572,20 @@ pub const Builder = struct {
|
|||
child.env_map = env_map;
|
||||
|
||||
const term = child.spawnAndWait() %% |err| {
|
||||
%%io.stderr.printf("Unable to spawn {}: {}\n", argv[0], @errorName(err));
|
||||
warn("Unable to spawn {}: {}\n", argv[0], @errorName(err));
|
||||
return err;
|
||||
};
|
||||
|
||||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
if (code != 0) {
|
||||
%%io.stderr.printf("The following command exited with error code {}:\n", code);
|
||||
warn("The following command exited with error code {}:\n", code);
|
||||
printCmd(cwd, argv);
|
||||
return error.UncleanExit;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
%%io.stderr.printf("The following command terminated unexpectedly:\n");
|
||||
warn("The following command terminated unexpectedly:\n");
|
||||
printCmd(cwd, argv);
|
||||
|
||||
return error.UncleanExit;
|
||||
|
@ -594,7 +596,7 @@ pub const Builder = struct {
|
|||
|
||||
pub fn makePath(self: &Builder, path: []const u8) -> %void {
|
||||
os.makePath(self.allocator, self.pathFromRoot(path)) %% |err| {
|
||||
%%io.stderr.printf("Unable to create path {}: {}\n", path, @errorName(err));
|
||||
warn("Unable to create path {}: {}\n", path, @errorName(err));
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
@ -633,17 +635,17 @@ pub const Builder = struct {
|
|||
|
||||
fn copyFileMode(self: &Builder, source_path: []const u8, dest_path: []const u8, mode: usize) -> %void {
|
||||
if (self.verbose) {
|
||||
%%io.stderr.printf("cp {} {}\n", source_path, dest_path);
|
||||
warn("cp {} {}\n", source_path, dest_path);
|
||||
}
|
||||
|
||||
const dirname = os.path.dirname(dest_path);
|
||||
const abs_source_path = self.pathFromRoot(source_path);
|
||||
os.makePath(self.allocator, dirname) %% |err| {
|
||||
%%io.stderr.printf("Unable to create path {}: {}\n", dirname, @errorName(err));
|
||||
warn("Unable to create path {}: {}\n", dirname, @errorName(err));
|
||||
return err;
|
||||
};
|
||||
os.copyFileMode(self.allocator, abs_source_path, dest_path, mode) %% |err| {
|
||||
%%io.stderr.printf("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err));
|
||||
warn("Unable to copy {} to {}: {}\n", abs_source_path, dest_path, @errorName(err));
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
@ -1103,7 +1105,7 @@ pub const LibExeObjStep = struct {
|
|||
assert(self.is_zig);
|
||||
|
||||
if (self.root_src == null and self.object_files.len == 0 and self.assembly_files.len == 0) {
|
||||
%%io.stderr.printf("{}: linker needs 1 or more objects to link\n", self.step.name);
|
||||
warn("{}: linker needs 1 or more objects to link\n", self.step.name);
|
||||
return error.NeedAnObject;
|
||||
}
|
||||
|
||||
|
@ -1799,11 +1801,11 @@ pub const WriteFileStep = struct {
|
|||
const full_path = self.builder.pathFromRoot(self.file_path);
|
||||
const full_path_dir = os.path.dirname(full_path);
|
||||
os.makePath(self.builder.allocator, full_path_dir) %% |err| {
|
||||
%%io.stderr.printf("unable to make path {}: {}\n", full_path_dir, @errorName(err));
|
||||
warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
|
||||
return err;
|
||||
};
|
||||
io.writeFile(full_path, self.data, self.builder.allocator) %% |err| {
|
||||
%%io.stderr.printf("unable to write {}: {}\n", full_path, @errorName(err));
|
||||
warn("unable to write {}: {}\n", full_path, @errorName(err));
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
@ -1824,8 +1826,7 @@ pub const LogStep = struct {
|
|||
|
||||
fn make(step: &Step) -> %void {
|
||||
const self = @fieldParentPtr(LogStep, "step", step);
|
||||
%%io.stderr.write(self.data);
|
||||
%%io.stderr.flush();
|
||||
warn("{}", self.data);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1847,7 +1848,7 @@ pub const RemoveDirStep = struct {
|
|||
|
||||
const full_path = self.builder.pathFromRoot(self.dir_path);
|
||||
os.deleteTree(self.builder.allocator, full_path) %% |err| {
|
||||
%%io.stderr.printf("Unable to remove {}: {}\n", full_path, @errorName(err));
|
||||
warn("Unable to remove {}: {}\n", full_path, @errorName(err));
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
@ -1896,13 +1897,13 @@ fn doAtomicSymLinks(allocator: &Allocator, output_path: []const u8, filename_maj
|
|||
// sym link for libfoo.so.1 to libfoo.so.1.2.3
|
||||
const major_only_path = %%os.path.join(allocator, out_dir, filename_major_only);
|
||||
os.atomicSymLink(allocator, out_basename, major_only_path) %% |err| {
|
||||
%%io.stderr.printf("Unable to symlink {} -> {}\n", major_only_path, out_basename);
|
||||
warn("Unable to symlink {} -> {}\n", major_only_path, out_basename);
|
||||
return err;
|
||||
};
|
||||
// sym link for libfoo.so to libfoo.so.1
|
||||
const name_only_path = %%os.path.join(allocator, out_dir, filename_name_only);
|
||||
os.atomicSymLink(allocator, filename_major_only, name_only_path) %% |err| {
|
||||
%%io.stderr.printf("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
|
||||
warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
|
199
std/debug.zig
199
std/debug.zig
|
@ -1,10 +1,11 @@
|
|||
const math = @import("math/index.zig");
|
||||
const mem = @import("mem.zig");
|
||||
const io = @import("io.zig");
|
||||
const os = @import("os/index.zig");
|
||||
const std = @import("index.zig");
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const io = std.io;
|
||||
const os = std.os;
|
||||
const elf = @import("elf.zig");
|
||||
const DW = @import("dwarf.zig");
|
||||
const ArrayList = @import("array_list.zig").ArrayList;
|
||||
const ArrayList = std.ArrayList;
|
||||
const builtin = @import("builtin");
|
||||
|
||||
error MissingDebugInfo;
|
||||
|
@ -12,10 +13,42 @@ error InvalidDebugInfo;
|
|||
error UnsupportedDebugInfo;
|
||||
|
||||
|
||||
/// Tries to write to stderr, unbuffered, and ignores any error returned.
|
||||
/// Does not append a newline.
|
||||
/// TODO atomic/multithread support
|
||||
var stderr_file: io.File = undefined;
|
||||
var stderr_stream: ?&io.OutStream = null;
|
||||
pub fn warn(comptime fmt: []const u8, args: ...) {
|
||||
const stderr = getStderrStream() %% return;
|
||||
stderr.print(fmt, args) %% return;
|
||||
}
|
||||
fn getStderrStream() -> %&io.OutStream {
|
||||
if (stderr_stream) |st| {
|
||||
return st;
|
||||
} else {
|
||||
stderr_file = %return io.getStdErr();
|
||||
const st = &stderr_file.out_stream;
|
||||
stderr_stream = st;
|
||||
return st;
|
||||
};
|
||||
}
|
||||
|
||||
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
|
||||
pub fn dumpStackTrace() {
|
||||
const stderr = getStderrStream() %% return;
|
||||
writeStackTrace(stderr, &global_allocator, stderr_file.isTty(), 1) %% return;
|
||||
}
|
||||
|
||||
/// This function invokes undefined behavior when `ok` is `false`.
|
||||
/// In Debug and ReleaseSafe modes, calls to this function are always
|
||||
/// generated, and the `unreachable` statement triggers a panic.
|
||||
/// In ReleaseFast and ReleaseSmall modes, calls to this function can be
|
||||
/// optimized away.
|
||||
pub fn assert(ok: bool) {
|
||||
if (!ok) {
|
||||
// In ReleaseFast test mode, we still want assert(false) to crash, so
|
||||
// we insert an explicit call to @panic instead of unreachable.
|
||||
// TODO we should use `assertOrPanic` in tests and remove this logic.
|
||||
if (builtin.is_test) {
|
||||
@panic("assertion failure")
|
||||
} else {
|
||||
|
@ -24,6 +57,14 @@ pub fn assert(ok: bool) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Call this function when you want to panic if the condition is not true.
|
||||
/// If `ok` is `false`, this function will panic in every release mode.
|
||||
pub fn assertOrPanic(ok: bool) {
|
||||
if (!ok) {
|
||||
@panic("assertion failure");
|
||||
}
|
||||
}
|
||||
|
||||
var panicking = false;
|
||||
/// This is the default panic implementation.
|
||||
pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
|
||||
|
@ -41,18 +82,13 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
|
|||
panicking = true;
|
||||
}
|
||||
|
||||
%%io.stderr.printf(format ++ "\n", args);
|
||||
%%writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty() %% false, 1);
|
||||
%%io.stderr.flush();
|
||||
const stderr = getStderrStream() %% os.abort();
|
||||
stderr.print(format ++ "\n", args) %% os.abort();
|
||||
writeStackTrace(stderr, &global_allocator, stderr_file.isTty(), 1) %% os.abort();
|
||||
|
||||
os.abort();
|
||||
}
|
||||
|
||||
pub fn printStackTrace() -> %void {
|
||||
%return writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty() %% false, 1);
|
||||
%return io.stderr.flush();
|
||||
}
|
||||
|
||||
const GREEN = "\x1b[32;1m";
|
||||
const WHITE = "\x1b[37;1m";
|
||||
const DIM = "\x1b[2m";
|
||||
|
@ -69,7 +105,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
|
|||
switch (builtin.object_format) {
|
||||
builtin.ObjectFormat.elf => {
|
||||
var stack_trace = ElfStackTrace {
|
||||
.self_exe_stream = undefined,
|
||||
.self_exe_file = undefined,
|
||||
.elf = undefined,
|
||||
.debug_info = undefined,
|
||||
.debug_abbrev = undefined,
|
||||
|
@ -79,10 +115,10 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
|
|||
.compile_unit_list = ArrayList(CompileUnit).init(allocator),
|
||||
};
|
||||
const st = &stack_trace;
|
||||
st.self_exe_stream = %return io.openSelfExe();
|
||||
defer st.self_exe_stream.close();
|
||||
st.self_exe_file = %return os.openSelfExe();
|
||||
defer st.self_exe_file.close();
|
||||
|
||||
%return st.elf.openStream(allocator, &st.self_exe_stream);
|
||||
%return st.elf.openFile(allocator, &st.self_exe_file);
|
||||
defer st.elf.close();
|
||||
|
||||
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
|
|||
const compile_unit = findCompileUnit(st, return_address) ?? {
|
||||
%return out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
|
||||
return_address);
|
||||
%return out_stream.flush();
|
||||
continue;
|
||||
};
|
||||
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
|
|||
},
|
||||
else => return err,
|
||||
};
|
||||
%return out_stream.flush();
|
||||
}
|
||||
},
|
||||
builtin.ObjectFormat.coff => {
|
||||
|
@ -158,7 +192,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
|
|||
}
|
||||
|
||||
fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) -> %void {
|
||||
var f = %return io.InStream.open(line_info.file_name, allocator);
|
||||
var f = %return io.File.openRead(line_info.file_name, allocator);
|
||||
defer f.close();
|
||||
// TODO fstat and make sure that the file has the correct size
|
||||
|
||||
|
@ -167,7 +201,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
|
|||
var column: usize = 1;
|
||||
var abs_index: usize = 0;
|
||||
while (true) {
|
||||
const amt_read = %return f.read(buf[0..]);
|
||||
const amt_read = %return f.in_stream.read(buf[0..]);
|
||||
const slice = buf[0..amt_read];
|
||||
|
||||
for (slice) |byte| {
|
||||
|
@ -191,7 +225,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
|
|||
}
|
||||
|
||||
const ElfStackTrace = struct {
|
||||
self_exe_stream: io.InStream,
|
||||
self_exe_file: io.File,
|
||||
elf: elf.Elf,
|
||||
debug_info: &elf.SectionHeader,
|
||||
debug_abbrev: &elf.SectionHeader,
|
||||
|
@ -205,7 +239,7 @@ const ElfStackTrace = struct {
|
|||
}
|
||||
|
||||
pub fn readString(self: &ElfStackTrace) -> %[]u8 {
|
||||
return readStringRaw(self.allocator(), &self.self_exe_stream);
|
||||
return readStringRaw(self.allocator(), &self.self_exe_file.in_stream);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -424,7 +458,7 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) -> %[]u8 {
|
|||
|
||||
fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 {
|
||||
const pos = st.debug_str.offset + offset;
|
||||
%return st.self_exe_stream.seekTo(pos);
|
||||
%return st.self_exe_file.seekTo(pos);
|
||||
return st.readString();
|
||||
}
|
||||
|
||||
|
@ -533,7 +567,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
|
|||
}
|
||||
|
||||
fn parseAbbrevTable(st: &ElfStackTrace) -> %AbbrevTable {
|
||||
const in_stream = &st.self_exe_stream;
|
||||
const in_stream = &st.self_exe_file.in_stream;
|
||||
var result = AbbrevTable.init(st.allocator());
|
||||
while (true) {
|
||||
const abbrev_code = %return readULeb128(in_stream);
|
||||
|
@ -568,7 +602,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&const AbbrevTable
|
|||
return &header.table;
|
||||
}
|
||||
}
|
||||
%return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset);
|
||||
%return st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset);
|
||||
%return st.abbrev_table_list.append(AbbrevTableHeader {
|
||||
.offset = abbrev_offset,
|
||||
.table = %return parseAbbrevTable(st),
|
||||
|
@ -585,8 +619,8 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?&
|
|||
}
|
||||
|
||||
fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die {
|
||||
const in_stream = &st.self_exe_stream;
|
||||
const abbrev_code = %return readULeb128(in_stream);
|
||||
const in_file = &st.self_exe_file;
|
||||
const abbrev_code = %return readULeb128(&in_file.in_stream);
|
||||
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo;
|
||||
|
||||
var result = Die {
|
||||
|
@ -598,7 +632,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -
|
|||
for (table_entry.attrs.toSliceConst()) |attr, i| {
|
||||
result.attrs.items[i] = Die.Attr {
|
||||
.id = attr.attr_id,
|
||||
.value = %return parseFormValue(st.allocator(), &st.self_exe_stream, attr.form_id, is_64),
|
||||
.value = %return parseFormValue(st.allocator(), &st.self_exe_file.in_stream, attr.form_id, is_64),
|
||||
};
|
||||
}
|
||||
return result;
|
||||
|
@ -607,16 +641,16 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -
|
|||
fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) -> %LineInfo {
|
||||
const compile_unit_cwd = %return compile_unit.die.getAttrString(st, DW.AT_comp_dir);
|
||||
|
||||
const in_stream = &st.self_exe_stream;
|
||||
const in_file = &st.self_exe_file;
|
||||
const debug_line_end = st.debug_line.offset + st.debug_line.size;
|
||||
var this_offset = st.debug_line.offset;
|
||||
var this_index: usize = 0;
|
||||
|
||||
while (this_offset < debug_line_end) : (this_index += 1) {
|
||||
%return in_stream.seekTo(this_offset);
|
||||
%return in_file.seekTo(this_offset);
|
||||
|
||||
var is_64: bool = undefined;
|
||||
const unit_length = %return readInitialLength(in_stream, &is_64);
|
||||
const unit_length = %return readInitialLength(&in_file.in_stream, &is_64);
|
||||
if (unit_length == 0)
|
||||
return error.MissingDebugInfo;
|
||||
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
|
|||
continue;
|
||||
}
|
||||
|
||||
const version = %return in_stream.readInt(st.elf.is_big_endian, u16);
|
||||
const version = %return in_file.in_stream.readInt(st.elf.is_big_endian, u16);
|
||||
if (version != 2) return error.InvalidDebugInfo;
|
||||
|
||||
const prologue_length = %return in_stream.readInt(st.elf.is_big_endian, u32);
|
||||
const prog_start_offset = (%return in_stream.getPos()) + prologue_length;
|
||||
const prologue_length = %return in_file.in_stream.readInt(st.elf.is_big_endian, u32);
|
||||
const prog_start_offset = (%return in_file.getPos()) + prologue_length;
|
||||
|
||||
const minimum_instruction_length = %return in_stream.readByte();
|
||||
const minimum_instruction_length = %return in_file.in_stream.readByte();
|
||||
if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
|
||||
|
||||
const default_is_stmt = (%return in_stream.readByte()) != 0;
|
||||
const line_base = %return in_stream.readByteSigned();
|
||||
const default_is_stmt = (%return in_file.in_stream.readByte()) != 0;
|
||||
const line_base = %return in_file.in_stream.readByteSigned();
|
||||
|
||||
const line_range = %return in_stream.readByte();
|
||||
const line_range = %return in_file.in_stream.readByte();
|
||||
if (line_range == 0)
|
||||
return error.InvalidDebugInfo;
|
||||
|
||||
const opcode_base = %return in_stream.readByte();
|
||||
const opcode_base = %return in_file.in_stream.readByte();
|
||||
|
||||
const standard_opcode_lengths = %return st.allocator().alloc(u8, opcode_base - 1);
|
||||
|
||||
{var i: usize = 0; while (i < opcode_base - 1) : (i += 1) {
|
||||
standard_opcode_lengths[i] = %return in_stream.readByte();
|
||||
standard_opcode_lengths[i] = %return in_file.in_stream.readByte();
|
||||
}}
|
||||
|
||||
var include_directories = ArrayList([]u8).init(st.allocator());
|
||||
|
@ -667,9 +701,9 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
|
|||
const file_name = %return st.readString();
|
||||
if (file_name.len == 0)
|
||||
break;
|
||||
const dir_index = %return readULeb128(in_stream);
|
||||
const mtime = %return readULeb128(in_stream);
|
||||
const len_bytes = %return readULeb128(in_stream);
|
||||
const dir_index = %return readULeb128(&in_file.in_stream);
|
||||
const mtime = %return readULeb128(&in_file.in_stream);
|
||||
const len_bytes = %return readULeb128(&in_file.in_stream);
|
||||
%return file_entries.append(FileEntry {
|
||||
.file_name = file_name,
|
||||
.dir_index = dir_index,
|
||||
|
@ -678,42 +712,32 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
|
|||
});
|
||||
}
|
||||
|
||||
%return in_stream.seekTo(prog_start_offset);
|
||||
%return in_file.seekTo(prog_start_offset);
|
||||
|
||||
while (true) {
|
||||
//const pos = (%return in_stream.getPos()) - this_offset;
|
||||
//if (pos == 0x1a3) @breakpoint();
|
||||
//%%io.stderr.printf("\n{x8}\n", pos);
|
||||
|
||||
const opcode = %return in_stream.readByte();
|
||||
const opcode = %return in_file.in_stream.readByte();
|
||||
|
||||
var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash
|
||||
if (opcode == DW.LNS_extended_op) {
|
||||
const op_size = %return readULeb128(in_stream);
|
||||
const op_size = %return readULeb128(&in_file.in_stream);
|
||||
if (op_size < 1)
|
||||
return error.InvalidDebugInfo;
|
||||
sub_op = %return in_stream.readByte();
|
||||
sub_op = %return in_file.in_stream.readByte();
|
||||
switch (sub_op) {
|
||||
DW.LNE_end_sequence => {
|
||||
//%%io.stdout.printf(" [0x{x8}] End Sequence\n", pos);
|
||||
prog.end_sequence = true;
|
||||
if (%return prog.checkLineMatch()) |info| return info;
|
||||
return error.MissingDebugInfo;
|
||||
},
|
||||
DW.LNE_set_address => {
|
||||
const addr = %return in_stream.readInt(st.elf.is_big_endian, usize);
|
||||
const addr = %return in_file.in_stream.readInt(st.elf.is_big_endian, usize);
|
||||
prog.address = addr;
|
||||
|
||||
//%%io.stdout.printf(" [0x{x8}] Extended opcode {}: set Address to 0x{x}\n",
|
||||
// pos, sub_op, addr);
|
||||
},
|
||||
DW.LNE_define_file => {
|
||||
//%%io.stdout.printf(" [0x{x8}] Define File\n", pos);
|
||||
|
||||
const file_name = %return st.readString();
|
||||
const dir_index = %return readULeb128(in_stream);
|
||||
const mtime = %return readULeb128(in_stream);
|
||||
const len_bytes = %return readULeb128(in_stream);
|
||||
const dir_index = %return readULeb128(&in_file.in_stream);
|
||||
const mtime = %return readULeb128(&in_file.in_stream);
|
||||
const len_bytes = %return readULeb128(&in_file.in_stream);
|
||||
%return file_entries.append(FileEntry {
|
||||
.file_name = file_name,
|
||||
.dir_index = dir_index,
|
||||
|
@ -723,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
|
|||
},
|
||||
else => {
|
||||
const fwd_amt = math.cast(isize, op_size - 1) %% return error.InvalidDebugInfo;
|
||||
%return in_stream.seekForward(fwd_amt);
|
||||
%return in_file.seekForward(fwd_amt);
|
||||
},
|
||||
}
|
||||
} else if (opcode >= opcode_base) {
|
||||
|
@ -733,48 +757,32 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
|
|||
const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
|
||||
prog.line += inc_line;
|
||||
prog.address += inc_addr;
|
||||
//%%io.stdout.printf(
|
||||
// " [0x{x8}] Special opcode {}: advance Address by {} to 0x{x} and Line by {} to {}\n",
|
||||
// pos, adjusted_opcode, inc_addr, prog.address, inc_line, prog.line);
|
||||
if (%return prog.checkLineMatch()) |info| return info;
|
||||
prog.basic_block = false;
|
||||
} else {
|
||||
switch (opcode) {
|
||||
DW.LNS_copy => {
|
||||
//%%io.stdout.printf(" [0x{x8}] Copy\n", pos);
|
||||
|
||||
if (%return prog.checkLineMatch()) |info| return info;
|
||||
prog.basic_block = false;
|
||||
},
|
||||
DW.LNS_advance_pc => {
|
||||
const arg = %return readULeb128(in_stream);
|
||||
const arg = %return readULeb128(&in_file.in_stream);
|
||||
prog.address += arg * minimum_instruction_length;
|
||||
|
||||
//%%io.stdout.printf(" [0x{x8}] Advance PC by {} to 0x{x}\n", pos, arg, prog.address);
|
||||
},
|
||||
DW.LNS_advance_line => {
|
||||
const arg = %return readILeb128(in_stream);
|
||||
const arg = %return readILeb128(&in_file.in_stream);
|
||||
prog.line += arg;
|
||||
|
||||
//%%io.stdout.printf(" [0x{x8}] Advance Line by {} to {}\n", pos, arg, prog.line);
|
||||
},
|
||||
DW.LNS_set_file => {
|
||||
const arg = %return readULeb128(in_stream);
|
||||
const arg = %return readULeb128(&in_file.in_stream);
|
||||
prog.file = arg;
|
||||
|
||||
//%%io.stdout.printf(" [0x{x8}] Set File Name to entry {} in the File Name Table\n",
|
||||
// pos, arg);
|
||||
},
|
||||
DW.LNS_set_column => {
|
||||
const arg = %return readULeb128(in_stream);
|
||||
const arg = %return readULeb128(&in_file.in_stream);
|
||||
prog.column = arg;
|
||||
|
||||
//%%io.stdout.printf(" [0x{x8}] Set column to {}\n", pos, arg);
|
||||
},
|
||||
DW.LNS_negate_stmt => {
|
||||
prog.is_stmt = !prog.is_stmt;
|
||||
|
||||
//%%io.stdout.printf(" [0x{x8}] Set is_stmt to {}\n", pos, if (prog.is_stmt) u8(1) else u8(0));
|
||||
},
|
||||
DW.LNS_set_basic_block => {
|
||||
prog.basic_block = true;
|
||||
|
@ -782,23 +790,18 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
|
|||
DW.LNS_const_add_pc => {
|
||||
const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
|
||||
prog.address += inc_addr;
|
||||
|
||||
//%%io.stdout.printf(" [0x{x8}] Advance PC by constant {} to 0x{x}\n",
|
||||
// pos, inc_addr, prog.address);
|
||||
},
|
||||
DW.LNS_fixed_advance_pc => {
|
||||
const arg = %return in_stream.readInt(st.elf.is_big_endian, u16);
|
||||
const arg = %return in_file.in_stream.readInt(st.elf.is_big_endian, u16);
|
||||
prog.address += arg;
|
||||
},
|
||||
DW.LNS_set_prologue_end => {
|
||||
//%%io.stdout.printf(" [0x{x8}] Set prologue_end to true\n", pos);
|
||||
},
|
||||
else => {
|
||||
if (opcode - 1 >= standard_opcode_lengths.len)
|
||||
return error.InvalidDebugInfo;
|
||||
//%%io.stdout.printf(" [0x{x8}] unknown op code {}\n", pos, opcode);
|
||||
const len_bytes = standard_opcode_lengths[opcode - 1];
|
||||
%return in_stream.seekForward(len_bytes);
|
||||
%return in_file.seekForward(len_bytes);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -815,30 +818,30 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
|
|||
var this_unit_offset = st.debug_info.offset;
|
||||
var cu_index: usize = 0;
|
||||
while (this_unit_offset < debug_info_end) {
|
||||
%return st.self_exe_stream.seekTo(this_unit_offset);
|
||||
%return st.self_exe_file.seekTo(this_unit_offset);
|
||||
|
||||
var is_64: bool = undefined;
|
||||
const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64);
|
||||
const unit_length = %return readInitialLength(&st.self_exe_file.in_stream, &is_64);
|
||||
if (unit_length == 0)
|
||||
return;
|
||||
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
|
||||
|
||||
const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16);
|
||||
const version = %return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u16);
|
||||
if (version < 2 or version > 5) return error.InvalidDebugInfo;
|
||||
|
||||
const debug_abbrev_offset = if (is_64) {
|
||||
%return st.self_exe_stream.readInt(st.elf.is_big_endian, u64)
|
||||
%return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u64)
|
||||
} else {
|
||||
%return st.self_exe_stream.readInt(st.elf.is_big_endian, u32)
|
||||
%return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u32)
|
||||
};
|
||||
|
||||
const address_size = %return st.self_exe_stream.readByte();
|
||||
const address_size = %return st.self_exe_file.in_stream.readByte();
|
||||
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
|
||||
|
||||
const compile_unit_pos = %return st.self_exe_stream.getPos();
|
||||
const compile_unit_pos = %return st.self_exe_file.getPos();
|
||||
const abbrev_table = %return getAbbrevTable(st, debug_abbrev_offset);
|
||||
|
||||
%return st.self_exe_stream.seekTo(compile_unit_pos);
|
||||
%return st.self_exe_file.seekTo(compile_unit_pos);
|
||||
|
||||
const compile_unit_die = %return st.allocator().create(Die);
|
||||
*compile_unit_die = %return parseDie(st, abbrev_table, is_64);
|
||||
|
|
120
std/elf.zig
120
std/elf.zig
|
@ -1,7 +1,9 @@
|
|||
const io = @import("io.zig");
|
||||
const math = @import("math/index.zig");
|
||||
const mem = @import("mem.zig");
|
||||
const debug = @import("debug.zig");
|
||||
const std = @import("index.zig");
|
||||
const io = std.io;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const debug = std.debug;
|
||||
const InStream = std.stream.InStream;
|
||||
|
||||
error InvalidFormat;
|
||||
|
||||
|
@ -62,7 +64,7 @@ pub const SectionHeader = struct {
|
|||
};
|
||||
|
||||
pub const Elf = struct {
|
||||
in_stream: &io.InStream,
|
||||
in_file: &io.File,
|
||||
auto_close_stream: bool,
|
||||
is_64: bool,
|
||||
is_big_endian: bool,
|
||||
|
@ -75,44 +77,44 @@ pub const Elf = struct {
|
|||
string_section: &SectionHeader,
|
||||
section_headers: []SectionHeader,
|
||||
allocator: &mem.Allocator,
|
||||
prealloc_stream: io.InStream,
|
||||
prealloc_file: io.File,
|
||||
|
||||
/// Call close when done.
|
||||
pub fn openFile(elf: &Elf, allocator: &mem.Allocator, path: []const u8) -> %void {
|
||||
%return elf.prealloc_stream.open(path);
|
||||
%return elf.openStream(allocator, &elf.prealloc_stream);
|
||||
pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) -> %void {
|
||||
%return elf.prealloc_file.open(path);
|
||||
%return elf.openFile(allocator, &elf.prealloc_file);
|
||||
elf.auto_close_stream = true;
|
||||
}
|
||||
|
||||
/// Call close when done.
|
||||
pub fn openStream(elf: &Elf, allocator: &mem.Allocator, stream: &io.InStream) -> %void {
|
||||
pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) -> %void {
|
||||
elf.allocator = allocator;
|
||||
elf.in_stream = stream;
|
||||
elf.in_file = file;
|
||||
elf.auto_close_stream = false;
|
||||
|
||||
var magic: [4]u8 = undefined;
|
||||
%return elf.in_stream.readNoEof(magic[0..]);
|
||||
%return elf.in_file.in_stream.readNoEof(magic[0..]);
|
||||
if (!mem.eql(u8, magic, "\x7fELF")) return error.InvalidFormat;
|
||||
|
||||
elf.is_64 = switch (%return elf.in_stream.readByte()) {
|
||||
elf.is_64 = switch (%return elf.in_file.in_stream.readByte()) {
|
||||
1 => false,
|
||||
2 => true,
|
||||
else => return error.InvalidFormat,
|
||||
};
|
||||
|
||||
elf.is_big_endian = switch (%return elf.in_stream.readByte()) {
|
||||
elf.is_big_endian = switch (%return elf.in_file.in_stream.readByte()) {
|
||||
1 => false,
|
||||
2 => true,
|
||||
else => return error.InvalidFormat,
|
||||
};
|
||||
|
||||
const version_byte = %return elf.in_stream.readByte();
|
||||
const version_byte = %return elf.in_file.in_stream.readByte();
|
||||
if (version_byte != 1) return error.InvalidFormat;
|
||||
|
||||
// skip over padding
|
||||
%return elf.in_stream.seekForward(9);
|
||||
%return elf.in_file.seekForward(9);
|
||||
|
||||
elf.file_type = switch (%return elf.in_stream.readInt(elf.is_big_endian, u16)) {
|
||||
elf.file_type = switch (%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16)) {
|
||||
1 => FileType.Relocatable,
|
||||
2 => FileType.Executable,
|
||||
3 => FileType.Shared,
|
||||
|
@ -120,7 +122,7 @@ pub const Elf = struct {
|
|||
else => return error.InvalidFormat,
|
||||
};
|
||||
|
||||
elf.arch = switch (%return elf.in_stream.readInt(elf.is_big_endian, u16)) {
|
||||
elf.arch = switch (%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16)) {
|
||||
0x02 => Arch.Sparc,
|
||||
0x03 => Arch.x86,
|
||||
0x08 => Arch.Mips,
|
||||
|
@ -133,34 +135,34 @@ pub const Elf = struct {
|
|||
else => return error.InvalidFormat,
|
||||
};
|
||||
|
||||
const elf_version = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
const elf_version = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
if (elf_version != 1) return error.InvalidFormat;
|
||||
|
||||
if (elf.is_64) {
|
||||
elf.entry_addr = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
elf.program_header_offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
elf.section_header_offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
elf.entry_addr = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
elf.program_header_offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
elf.section_header_offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
} else {
|
||||
elf.entry_addr = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
elf.program_header_offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
elf.section_header_offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
elf.entry_addr = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
elf.program_header_offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
elf.section_header_offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
}
|
||||
|
||||
// skip over flags
|
||||
%return elf.in_stream.seekForward(4);
|
||||
%return elf.in_file.seekForward(4);
|
||||
|
||||
const header_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
|
||||
const header_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
|
||||
if ((elf.is_64 and header_size != 64) or
|
||||
(!elf.is_64 and header_size != 52))
|
||||
{
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
const ph_entry_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
|
||||
const ph_entry_count = %return elf.in_stream.readInt(elf.is_big_endian, u16);
|
||||
const sh_entry_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
|
||||
const sh_entry_count = %return elf.in_stream.readInt(elf.is_big_endian, u16);
|
||||
elf.string_section_index = u64(%return elf.in_stream.readInt(elf.is_big_endian, u16));
|
||||
const ph_entry_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
|
||||
const ph_entry_count = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
|
||||
const sh_entry_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
|
||||
const sh_entry_count = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
|
||||
elf.string_section_index = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16));
|
||||
|
||||
if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
|
||||
|
||||
|
@ -169,12 +171,12 @@ pub const Elf = struct {
|
|||
const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count);
|
||||
const end_ph = %return math.add(u64, elf.program_header_offset, ph_byte_count);
|
||||
|
||||
const stream_end = %return elf.in_stream.getEndPos();
|
||||
const stream_end = %return elf.in_file.getEndPos();
|
||||
if (stream_end < end_sh or stream_end < end_ph) {
|
||||
return error.InvalidFormat;
|
||||
}
|
||||
|
||||
%return elf.in_stream.seekTo(elf.section_header_offset);
|
||||
%return elf.in_file.seekTo(elf.section_header_offset);
|
||||
|
||||
elf.section_headers = %return elf.allocator.alloc(SectionHeader, sh_entry_count);
|
||||
%defer elf.allocator.free(elf.section_headers);
|
||||
|
@ -183,32 +185,32 @@ pub const Elf = struct {
|
|||
if (sh_entry_size != 64) return error.InvalidFormat;
|
||||
|
||||
for (elf.section_headers) |*section| {
|
||||
section.name = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.sh_type = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.flags = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.addr = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.size = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.link = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.info = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.addr_align = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.ent_size = %return elf.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.name = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.sh_type = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.flags = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.addr = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.link = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.info = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.addr_align = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
section.ent_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
|
||||
}
|
||||
} else {
|
||||
if (sh_entry_size != 40) return error.InvalidFormat;
|
||||
|
||||
for (elf.section_headers) |*section| {
|
||||
// TODO (multiple occurences) allow implicit cast from %u32 -> %u64 ?
|
||||
section.name = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.sh_type = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.flags = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.addr = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.size = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.link = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.info = %return elf.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.addr_align = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.ent_size = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.name = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.sh_type = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.flags = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.addr = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.size = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.link = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.info = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
|
||||
section.addr_align = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
section.ent_size = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +232,7 @@ pub const Elf = struct {
|
|||
elf.allocator.free(elf.section_headers);
|
||||
|
||||
if (elf.auto_close_stream)
|
||||
elf.in_stream.close();
|
||||
elf.in_file.close();
|
||||
}
|
||||
|
||||
pub fn findSection(elf: &Elf, name: []const u8) -> %?&SectionHeader {
|
||||
|
@ -238,15 +240,15 @@ pub const Elf = struct {
|
|||
if (section.sh_type == SHT_NULL) continue;
|
||||
|
||||
const name_offset = elf.string_section.offset + section.name;
|
||||
%return elf.in_stream.seekTo(name_offset);
|
||||
%return elf.in_file.seekTo(name_offset);
|
||||
|
||||
for (name) |expected_c| {
|
||||
const target_c = %return elf.in_stream.readByte();
|
||||
const target_c = %return elf.in_file.in_stream.readByte();
|
||||
if (target_c == 0 or expected_c != target_c) goto next_section;
|
||||
}
|
||||
|
||||
{
|
||||
const null_byte = %return elf.in_stream.readByte();
|
||||
const null_byte = %return elf.in_file.in_stream.readByte();
|
||||
if (null_byte == 0) return section;
|
||||
}
|
||||
|
||||
|
@ -257,6 +259,6 @@ pub const Elf = struct {
|
|||
}
|
||||
|
||||
pub fn seekToSection(elf: &Elf, section: &SectionHeader) -> %void {
|
||||
%return elf.in_stream.seekTo(section.offset);
|
||||
%return elf.in_file.seekTo(section.offset);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,10 +19,10 @@ const State = enum { // TODO put inside format function and make sure the name a
|
|||
};
|
||||
|
||||
/// Renders fmt string with args, calling output with slices of bytes.
|
||||
/// Return false from output function and output will not be called again.
|
||||
/// Returns false if output ever returned false, true otherwise.
|
||||
pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
||||
comptime fmt: []const u8, args: ...) -> bool
|
||||
/// If `output` returns an error, the error is returned from `format` and
|
||||
/// `output` is not called again.
|
||||
pub fn format(context: var, output: fn(@typeOf(context), []const u8)->%void,
|
||||
comptime fmt: []const u8, args: ...) -> %void
|
||||
{
|
||||
comptime var start_index = 0;
|
||||
comptime var state = State.Start;
|
||||
|
@ -38,15 +38,13 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
|||
'{' => {
|
||||
// TODO if you make this an if statement with `and` then it breaks
|
||||
if (start_index < i) {
|
||||
if (!output(context, fmt[start_index..i]))
|
||||
return false;
|
||||
%return output(context, fmt[start_index..i]);
|
||||
}
|
||||
state = State.OpenBrace;
|
||||
},
|
||||
'}' => {
|
||||
if (start_index < i) {
|
||||
if (!output(context, fmt[start_index..i]))
|
||||
return false;
|
||||
%return output(context, fmt[start_index..i]);
|
||||
}
|
||||
state = State.CloseBrace;
|
||||
},
|
||||
|
@ -58,8 +56,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
|||
start_index = i;
|
||||
},
|
||||
'}' => {
|
||||
if (!formatValue(args[next_arg], context, output))
|
||||
return false;
|
||||
%return formatValue(args[next_arg], context, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
|
@ -109,8 +106,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
|||
},
|
||||
State.Integer => switch (c) {
|
||||
'}' => {
|
||||
if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
|
||||
return false;
|
||||
%return formatInt(args[next_arg], radix, uppercase, width, context, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
|
@ -124,8 +120,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
|||
State.IntegerWidth => switch (c) {
|
||||
'}' => {
|
||||
width = comptime %%parseUnsigned(usize, fmt[width_start..i], 10);
|
||||
if (!formatInt(args[next_arg], radix, uppercase, width, context, output))
|
||||
return false;
|
||||
%return formatInt(args[next_arg], radix, uppercase, width, context, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
|
@ -136,8 +131,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
|||
State.BufWidth => switch (c) {
|
||||
'}' => {
|
||||
width = comptime %%parseUnsigned(usize, fmt[width_start..i], 10);
|
||||
if (!formatBuf(args[next_arg], width, context, output))
|
||||
return false;
|
||||
%return formatBuf(args[next_arg], width, context, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
|
@ -147,8 +141,7 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
|||
},
|
||||
State.Character => switch (c) {
|
||||
'}' => {
|
||||
if (!formatAsciiChar(args[next_arg], context, output))
|
||||
return false;
|
||||
%return formatAsciiChar(args[next_arg], context, output);
|
||||
next_arg += 1;
|
||||
state = State.Start;
|
||||
start_index = i + 1;
|
||||
|
@ -166,14 +159,11 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
|
|||
}
|
||||
}
|
||||
if (start_index < fmt.len) {
|
||||
if (!output(context, fmt[start_index..]))
|
||||
return false;
|
||||
%return output(context, fmt[start_index..]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
|
||||
pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void {
|
||||
const T = @typeOf(value);
|
||||
switch (@typeId(T)) {
|
||||
builtin.TypeId.Int => {
|
||||
|
@ -203,8 +193,7 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
|
|||
}
|
||||
},
|
||||
builtin.TypeId.Error => {
|
||||
if (!output(context, "error."))
|
||||
return false;
|
||||
%return output(context, "error.");
|
||||
return output(context, @errorName(value));
|
||||
},
|
||||
builtin.TypeId.Pointer => {
|
||||
|
@ -223,27 +212,23 @@ pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []cons
|
|||
}
|
||||
}
|
||||
|
||||
pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
|
||||
pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void {
|
||||
return output(context, (&c)[0..1]);
|
||||
}
|
||||
|
||||
pub fn formatBuf(buf: []const u8, width: usize,
|
||||
context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
|
||||
context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
|
||||
{
|
||||
if (!output(context, buf))
|
||||
return false;
|
||||
%return output(context, buf);
|
||||
|
||||
var leftover_padding = if (width > buf.len) (width - buf.len) else return true;
|
||||
var leftover_padding = if (width > buf.len) (width - buf.len) else return;
|
||||
const pad_byte: u8 = ' ';
|
||||
while (leftover_padding > 0) : (leftover_padding -= 1) {
|
||||
if (!output(context, (&pad_byte)[0..1]))
|
||||
return false;
|
||||
%return output(context, (&pad_byte)[0..1]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool {
|
||||
pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void {
|
||||
var x = f64(value);
|
||||
|
||||
// Errol doesn't handle these special cases.
|
||||
|
@ -251,8 +236,7 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
|
|||
return output(context, "NaN");
|
||||
}
|
||||
if (math.signbit(x)) {
|
||||
if (!output(context, "-"))
|
||||
return false;
|
||||
%return output(context, "-");
|
||||
x = -x;
|
||||
}
|
||||
if (math.isPositiveInf(x)) {
|
||||
|
@ -264,34 +248,27 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
|
|||
|
||||
var buffer: [32]u8 = undefined;
|
||||
const float_decimal = errol3(x, buffer[0..]);
|
||||
if (!output(context, float_decimal.digits[0..1]))
|
||||
return false;
|
||||
if (!output(context, "."))
|
||||
return false;
|
||||
%return output(context, float_decimal.digits[0..1]);
|
||||
%return output(context, ".");
|
||||
if (float_decimal.digits.len > 1) {
|
||||
const num_digits = if (@typeOf(value) == f32) {
|
||||
math.min(usize(9), float_decimal.digits.len)
|
||||
} else {
|
||||
float_decimal.digits.len
|
||||
};
|
||||
if (!output(context, float_decimal.digits[1 .. num_digits]))
|
||||
return false;
|
||||
%return output(context, float_decimal.digits[1 .. num_digits]);
|
||||
} else {
|
||||
if (!output(context, "0"))
|
||||
return false;
|
||||
%return output(context, "0");
|
||||
}
|
||||
|
||||
if (float_decimal.exp != 1) {
|
||||
if (!output(context, "e"))
|
||||
return false;
|
||||
if (!formatInt(float_decimal.exp - 1, 10, false, 0, context, output))
|
||||
return false;
|
||||
%return output(context, "e");
|
||||
%return formatInt(float_decimal.exp - 1, 10, false, 0, context, output);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
|
||||
context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
|
||||
context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
|
||||
{
|
||||
if (@typeOf(value).is_signed) {
|
||||
return formatIntSigned(value, base, uppercase, width, context, output);
|
||||
|
@ -301,13 +278,12 @@ pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
|
|||
}
|
||||
|
||||
fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
|
||||
context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
|
||||
context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
|
||||
{
|
||||
const uint = @IntType(false, @typeOf(value).bit_count);
|
||||
if (value < 0) {
|
||||
const minus_sign: u8 = '-';
|
||||
if (!output(context, (&minus_sign)[0..1]))
|
||||
return false;
|
||||
%return output(context, (&minus_sign)[0..1]);
|
||||
const new_value = uint(-(value + 1)) + 1;
|
||||
const new_width = if (width == 0) 0 else (width - 1);
|
||||
return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
|
||||
|
@ -315,8 +291,7 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
|
|||
return formatIntUnsigned(uint(value), base, uppercase, width, context, output);
|
||||
} else {
|
||||
const plus_sign: u8 = '+';
|
||||
if (!output(context, (&plus_sign)[0..1]))
|
||||
return false;
|
||||
%return output(context, (&plus_sign)[0..1]);
|
||||
const new_value = uint(value);
|
||||
const new_width = if (width == 0) 0 else (width - 1);
|
||||
return formatIntUnsigned(new_value, base, uppercase, new_width, context, output);
|
||||
|
@ -324,7 +299,7 @@ fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize,
|
|||
}
|
||||
|
||||
fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
|
||||
context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
|
||||
context: var, output: fn(@typeOf(context), []const u8)->%void) -> %void
|
||||
{
|
||||
// max_int_digits accounts for the minus sign. when printing an unsigned
|
||||
// number we don't need to do that.
|
||||
|
@ -348,8 +323,7 @@ fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize,
|
|||
const zero_byte: u8 = '0';
|
||||
var leftover_padding = padding - index;
|
||||
while (true) {
|
||||
if (!output(context, (&zero_byte)[0..1]))
|
||||
return false;
|
||||
%return output(context, (&zero_byte)[0..1]);
|
||||
leftover_padding -= 1;
|
||||
if (leftover_padding == 0)
|
||||
break;
|
||||
|
@ -368,17 +342,16 @@ pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width:
|
|||
.out_buf = out_buf,
|
||||
.index = 0,
|
||||
};
|
||||
_ = formatInt(value, base, uppercase, width, &context, formatIntCallback);
|
||||
%%formatInt(value, base, uppercase, width, &context, formatIntCallback);
|
||||
return context.index;
|
||||
}
|
||||
const FormatIntBuf = struct {
|
||||
out_buf: []u8,
|
||||
index: usize,
|
||||
};
|
||||
fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) -> bool {
|
||||
fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) -> %void {
|
||||
mem.copy(u8, context.out_buf[context.index..], bytes);
|
||||
context.index += bytes.len;
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) -> %T {
|
||||
|
@ -440,28 +413,27 @@ const BufPrintContext = struct {
|
|||
remaining: []u8,
|
||||
};
|
||||
|
||||
fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> bool {
|
||||
fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> %void {
|
||||
mem.copy(u8, context.remaining, bytes);
|
||||
context.remaining = context.remaining[bytes.len..];
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) -> []u8 {
|
||||
var context = BufPrintContext { .remaining = buf, };
|
||||
_ = format(&context, bufPrintWrite, fmt, args);
|
||||
%%format(&context, bufPrintWrite, fmt, args);
|
||||
return buf[0..buf.len - context.remaining.len];
|
||||
}
|
||||
|
||||
pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) -> %[]u8 {
|
||||
var size: usize = 0;
|
||||
_ = format(&size, countSize, fmt, args);
|
||||
// Cannot fail because `countSize` cannot fail.
|
||||
%%format(&size, countSize, fmt, args);
|
||||
const buf = %return allocator.alloc(u8, size);
|
||||
return bufPrint(buf, fmt, args);
|
||||
}
|
||||
|
||||
fn countSize(size: &usize, bytes: []const u8) -> bool {
|
||||
fn countSize(size: &usize, bytes: []const u8) -> %void {
|
||||
*size += bytes.len;
|
||||
return true;
|
||||
}
|
||||
|
||||
test "buf print int" {
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
const debug = @import("debug.zig");
|
||||
const assert = debug.assert;
|
||||
const mem = @import("mem.zig");
|
||||
const os = @import("os/index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const c = @import("c/index.zig");
|
||||
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
error OutOfMemory;
|
||||
|
||||
pub var c_allocator = Allocator {
|
||||
.allocFn = cAlloc,
|
||||
.reallocFn = cRealloc,
|
||||
.freeFn = cFree,
|
||||
};
|
||||
|
||||
fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||
if (c.malloc(usize(n))) |mem| {
|
||||
@ptrCast(&u8, mem)[0..n]
|
||||
} else {
|
||||
error.OutOfMemory
|
||||
}
|
||||
}
|
||||
|
||||
fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
old_mem[0..new_size]
|
||||
} else {
|
||||
const old_ptr = @ptrCast(&c_void, old_mem.ptr);
|
||||
if (c.realloc(old_ptr, usize(new_size))) |mem| {
|
||||
@ptrCast(&u8, mem)[0..new_size]
|
||||
} else {
|
||||
error.OutOfMemory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cFree(self: &Allocator, old_mem: []u8) {
|
||||
const old_ptr = @ptrCast(&c_void, old_mem.ptr);
|
||||
c.free(old_ptr);
|
||||
}
|
||||
|
||||
pub const IncrementingAllocator = struct {
|
||||
allocator: Allocator,
|
||||
bytes: []u8,
|
||||
end_index: usize,
|
||||
heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
|
||||
|
||||
fn init(capacity: usize) -> %IncrementingAllocator {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin, Os.macosx, Os.ios => {
|
||||
const p = os.posix;
|
||||
const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
|
||||
p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
|
||||
if (addr == p.MAP_FAILED) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
return IncrementingAllocator {
|
||||
.allocator = Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
.bytes = @intToPtr(&u8, addr)[0..capacity],
|
||||
.end_index = 0,
|
||||
.heap_handle = {},
|
||||
};
|
||||
},
|
||||
Os.windows => {
|
||||
const heap_handle = os.windows.GetProcessHeap() ?? return error.OutOfMemory;
|
||||
const ptr = os.windows.HeapAlloc(heap_handle, 0, capacity) ?? return error.OutOfMemory;
|
||||
return IncrementingAllocator {
|
||||
.allocator = Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
.bytes = @ptrCast(&u8, ptr)[0..capacity],
|
||||
.end_index = 0,
|
||||
.heap_handle = heap_handle,
|
||||
};
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
fn deinit(self: &IncrementingAllocator) {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin, Os.macosx, Os.ios => {
|
||||
_ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
|
||||
},
|
||||
Os.windows => {
|
||||
_ = os.windows.HeapFree(self.heap_handle, 0, @ptrCast(os.windows.LPVOID, self.bytes.ptr));
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(self: &IncrementingAllocator) {
|
||||
self.end_index = 0;
|
||||
}
|
||||
|
||||
fn bytesLeft(self: &const IncrementingAllocator) -> usize {
|
||||
return self.bytes.len - self.end_index;
|
||||
}
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
|
||||
const addr = @ptrToInt(&self.bytes[self.end_index]);
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = self.end_index + march_forward_bytes;
|
||||
const new_end_index = adjusted_index + n;
|
||||
if (new_end_index > self.bytes.len) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
const result = self.bytes[adjusted_index .. new_end_index];
|
||||
self.end_index = new_end_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = %return alloc(allocator, new_size, alignment);
|
||||
mem.copy(u8, result, old_mem);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn free(allocator: &Allocator, bytes: []u8) {
|
||||
// Do nothing. That's the point of an incrementing allocator.
|
||||
}
|
||||
};
|
||||
|
||||
test "IncrementingAllocator" {
|
||||
const total_bytes = 100 * 1024 * 1024;
|
||||
var inc_allocator = %%IncrementingAllocator.init(total_bytes);
|
||||
defer inc_allocator.deinit();
|
||||
|
||||
const allocator = &inc_allocator.allocator;
|
||||
const slice = %%allocator.alloc(&i32, 100);
|
||||
|
||||
for (slice) |*item, i| {
|
||||
*item = %%allocator.create(i32);
|
||||
**item = i32(i);
|
||||
}
|
||||
|
||||
assert(inc_allocator.bytesLeft() == total_bytes - @sizeOf(i32) * 100 - @sizeOf(usize) * 100);
|
||||
|
||||
inc_allocator.reset();
|
||||
|
||||
assert(inc_allocator.bytesLeft() == total_bytes);
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ pub const elf = @import("elf.zig");
|
|||
pub const empty_import = @import("empty.zig");
|
||||
pub const endian = @import("endian.zig");
|
||||
pub const fmt = @import("fmt/index.zig");
|
||||
pub const heap = @import("heap.zig");
|
||||
pub const io = @import("io.zig");
|
||||
pub const math = @import("math/index.zig");
|
||||
pub const mem = @import("mem.zig");
|
||||
|
@ -45,6 +46,7 @@ test "std" {
|
|||
_ = @import("io.zig");
|
||||
_ = @import("math/index.zig");
|
||||
_ = @import("mem.zig");
|
||||
_ = @import("heap.zig");
|
||||
_ = @import("net.zig");
|
||||
_ = @import("os/index.zig");
|
||||
_ = @import("rand.zig");
|
||||
|
|
670
std/io.zig
670
std/io.zig
|
@ -1,3 +1,4 @@
|
|||
const std = @import("index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const system = switch(builtin.os) {
|
||||
|
@ -6,41 +7,19 @@ const system = switch(builtin.os) {
|
|||
Os.windows => @import("os/windows/index.zig"),
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
const c = @import("c/index.zig");
|
||||
const c = std.c;
|
||||
|
||||
const math = @import("math/index.zig");
|
||||
const debug = @import("debug.zig");
|
||||
const math = std.math;
|
||||
const debug = std.debug;
|
||||
const assert = debug.assert;
|
||||
const os = @import("os/index.zig");
|
||||
const mem = @import("mem.zig");
|
||||
const Buffer = @import("buffer.zig").Buffer;
|
||||
const fmt = @import("fmt/index.zig");
|
||||
const os = std.os;
|
||||
const mem = std.mem;
|
||||
const Buffer = std.Buffer;
|
||||
const fmt = std.fmt;
|
||||
|
||||
const is_posix = builtin.os != builtin.Os.windows;
|
||||
const is_windows = builtin.os == builtin.Os.windows;
|
||||
|
||||
pub var stdin = InStream {
|
||||
.fd = if (is_posix) system.STDIN_FILENO else {},
|
||||
.handle_id = if (is_windows) system.STD_INPUT_HANDLE else {},
|
||||
.handle = if (is_windows) null else {},
|
||||
};
|
||||
|
||||
pub var stdout = OutStream {
|
||||
.fd = if (is_posix) system.STDOUT_FILENO else {},
|
||||
.handle_id = if (is_windows) system.STD_OUTPUT_HANDLE else {},
|
||||
.handle = if (is_windows) null else {},
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
};
|
||||
|
||||
pub var stderr = OutStream {
|
||||
.fd = if (is_posix) system.STDERR_FILENO else {},
|
||||
.handle_id = if (is_windows) system.STD_ERROR_HANDLE else {},
|
||||
.handle = if (is_windows) null else {},
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
};
|
||||
|
||||
/// The function received invalid input at runtime. An Invalid error means a
|
||||
/// bug in the program that called the function.
|
||||
error Invalid;
|
||||
|
@ -63,18 +42,72 @@ error PathNotFound;
|
|||
error OutOfMemory;
|
||||
error Unseekable;
|
||||
error EndOfFile;
|
||||
error NoStdHandles;
|
||||
|
||||
pub const OutStream = struct {
|
||||
fd: if (is_posix) i32 else void,
|
||||
handle_id: if (is_windows) system.DWORD else void,
|
||||
handle: if (is_windows) ?system.HANDLE else void,
|
||||
buffer: [os.page_size]u8,
|
||||
index: usize,
|
||||
pub fn getStdErr() -> %File {
|
||||
const handle = if (is_windows) {
|
||||
%return os.windowsGetStdHandle(system.STD_ERROR_HANDLE)
|
||||
} else if (is_posix) {
|
||||
system.STDERR_FILENO
|
||||
} else {
|
||||
unreachable
|
||||
};
|
||||
return File.openHandle(handle);
|
||||
}
|
||||
|
||||
/// Calls ::openMode with 0o666 for the mode.
|
||||
pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %OutStream {
|
||||
return openMode(path, 0o666, allocator);
|
||||
pub fn getStdOut() -> %File {
|
||||
const handle = if (is_windows) {
|
||||
%return os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE)
|
||||
} else if (is_posix) {
|
||||
system.STDOUT_FILENO
|
||||
} else {
|
||||
unreachable
|
||||
};
|
||||
return File.openHandle(handle);
|
||||
}
|
||||
|
||||
pub fn getStdIn() -> %File {
|
||||
const handle = if (is_windows) {
|
||||
%return os.windowsGetStdHandle(system.STD_INPUT_HANDLE)
|
||||
} else if (is_posix) {
|
||||
system.STDIN_FILENO
|
||||
} else {
|
||||
unreachable
|
||||
};
|
||||
return File.openHandle(handle);
|
||||
}
|
||||
|
||||
pub const File = struct {
|
||||
/// The OS-specific file descriptor or file handle.
|
||||
handle: os.FileHandle,
|
||||
|
||||
/// A file has the `InStream` trait
|
||||
in_stream: InStream,
|
||||
|
||||
/// A file has the `OutStream` trait
|
||||
out_stream: OutStream,
|
||||
|
||||
/// `path` may need to be copied in memory to add a null terminating byte. In this case
|
||||
/// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
|
||||
/// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
|
||||
/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
|
||||
/// Call close to clean up.
|
||||
pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) -> %File {
|
||||
if (is_posix) {
|
||||
const flags = system.O_LARGEFILE|system.O_RDONLY;
|
||||
const fd = %return os.posixOpen(path, flags, 0, allocator);
|
||||
return openHandle(fd);
|
||||
} else if (is_windows) {
|
||||
const handle = %return os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ,
|
||||
system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator);
|
||||
return openHandle(handle);
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls `openWriteMode` with 0o666 for the mode.
|
||||
pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) -> %File {
|
||||
return openWriteMode(path, 0o666, allocator);
|
||||
|
||||
}
|
||||
|
||||
|
@ -83,286 +116,51 @@ pub const OutStream = struct {
|
|||
/// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
|
||||
/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
|
||||
/// Call close to clean up.
|
||||
pub fn openMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) -> %OutStream {
|
||||
pub fn openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) -> %File {
|
||||
if (is_posix) {
|
||||
const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC;
|
||||
const fd = %return os.posixOpen(path, flags, mode, allocator);
|
||||
return OutStream {
|
||||
.fd = fd,
|
||||
.handle = {},
|
||||
.handle_id = {},
|
||||
.index = 0,
|
||||
.buffer = undefined,
|
||||
};
|
||||
return openHandle(fd);
|
||||
} else if (is_windows) {
|
||||
const handle = %return os.windowsOpen(path, system.GENERIC_WRITE,
|
||||
system.FILE_SHARE_WRITE|system.FILE_SHARE_READ|system.FILE_SHARE_DELETE,
|
||||
system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL, allocator);
|
||||
return OutStream {
|
||||
.fd = {},
|
||||
.handle = handle,
|
||||
.handle_id = undefined,
|
||||
.index = 0,
|
||||
.buffer = undefined,
|
||||
};
|
||||
|
||||
return openHandle(handle);
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn writeByte(self: &OutStream, b: u8) -> %void {
|
||||
if (self.buffer.len == self.index) %return self.flush();
|
||||
self.buffer[self.index] = b;
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
pub fn write(self: &OutStream, bytes: []const u8) -> %void {
|
||||
if (bytes.len >= self.buffer.len) {
|
||||
%return self.flush();
|
||||
return self.unbufferedWrite(bytes);
|
||||
}
|
||||
|
||||
var src_index: usize = 0;
|
||||
|
||||
while (src_index < bytes.len) {
|
||||
const dest_space_left = self.buffer.len - self.index;
|
||||
const copy_amt = math.min(dest_space_left, bytes.len - src_index);
|
||||
mem.copy(u8, self.buffer[self.index..], bytes[src_index..src_index + copy_amt]);
|
||||
self.index += copy_amt;
|
||||
assert(self.index <= self.buffer.len);
|
||||
if (self.index == self.buffer.len) {
|
||||
%return self.flush();
|
||||
}
|
||||
src_index += copy_amt;
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls print and then flushes the buffer.
|
||||
pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
|
||||
%return self.print(format, args);
|
||||
%return self.flush();
|
||||
}
|
||||
|
||||
/// Does not flush the buffer.
|
||||
pub fn print(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
|
||||
var context = PrintContext {
|
||||
.self = self,
|
||||
.result = {},
|
||||
pub fn openHandle(handle: os.FileHandle) -> File {
|
||||
return File {
|
||||
.handle = handle,
|
||||
.out_stream = OutStream {
|
||||
.writeFn = writeFn,
|
||||
},
|
||||
.in_stream = InStream {
|
||||
.readFn = readFn,
|
||||
},
|
||||
};
|
||||
_ = fmt.format(&context, printOutput, format, args);
|
||||
return context.result;
|
||||
}
|
||||
const PrintContext = struct {
|
||||
self: &OutStream,
|
||||
result: %void,
|
||||
};
|
||||
fn printOutput(context: &PrintContext, bytes: []const u8) -> bool {
|
||||
context.self.write(bytes) %% |err| {
|
||||
context.result = err;
|
||||
return false;
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn flush(self: &OutStream) -> %void {
|
||||
if (self.index == 0)
|
||||
return;
|
||||
|
||||
%return self.unbufferedWrite(self.buffer[0..self.index]);
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
pub fn close(self: &OutStream) {
|
||||
assert(self.index == 0); // unflushed buffer
|
||||
if (is_posix) {
|
||||
os.posixClose(self.fd);
|
||||
} else if (is_windows) {
|
||||
os.windowsClose(%%self.getHandle());
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isTty(self: &OutStream) -> %bool {
|
||||
if (is_posix) {
|
||||
if (builtin.link_libc) {
|
||||
return c.isatty(self.fd) != 0;
|
||||
} else {
|
||||
return system.isatty(self.fd);
|
||||
}
|
||||
} else if (is_windows) {
|
||||
return os.windowsIsTty(%return self.getHandle());
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
fn getHandle(self: &OutStream) -> %system.HANDLE {
|
||||
if (self.handle) |handle| return handle;
|
||||
if (system.GetStdHandle(self.handle_id)) |handle| {
|
||||
if (handle == system.INVALID_HANDLE_VALUE) {
|
||||
const err = system.GetLastError();
|
||||
return switch (err) {
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
self.handle = handle;
|
||||
return handle;
|
||||
} else {
|
||||
return error.NoStdHandles;
|
||||
}
|
||||
}
|
||||
|
||||
fn unbufferedWrite(self: &OutStream, bytes: []const u8) -> %void {
|
||||
if (is_posix) {
|
||||
%return os.posixWrite(self.fd, bytes);
|
||||
} else if (is_windows) {
|
||||
const handle = %return self.getHandle();
|
||||
%return os.windowsWrite(handle, bytes);
|
||||
} else {
|
||||
@compileError("Unsupported OS");
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// TODO created a BufferedInStream struct and move some of this code there
|
||||
// BufferedInStream API goes on top of minimal InStream API.
|
||||
pub const InStream = struct {
|
||||
fd: if (is_posix) i32 else void,
|
||||
handle_id: if (is_windows) system.DWORD else void,
|
||||
handle: if (is_windows) ?system.HANDLE else void,
|
||||
|
||||
/// `path` may need to be copied in memory to add a null terminating byte. In this case
|
||||
/// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
|
||||
/// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
|
||||
/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
|
||||
/// Call close to clean up.
|
||||
pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %InStream {
|
||||
if (is_posix) {
|
||||
const flags = system.O_LARGEFILE|system.O_RDONLY;
|
||||
const fd = %return os.posixOpen(path, flags, 0, allocator);
|
||||
return InStream {
|
||||
.fd = fd,
|
||||
.handle_id = {},
|
||||
.handle = {},
|
||||
};
|
||||
} else if (is_windows) {
|
||||
const handle = %return os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ,
|
||||
system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator);
|
||||
return InStream {
|
||||
.fd = {},
|
||||
.handle_id = undefined,
|
||||
.handle = handle,
|
||||
};
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
/// Upon success, the stream is in an uninitialized state. To continue using it,
|
||||
/// you must use the open() function.
|
||||
pub fn close(self: &InStream) {
|
||||
if (is_posix) {
|
||||
os.posixClose(self.fd);
|
||||
} else if (is_windows) {
|
||||
os.windowsClose(%%self.getHandle());
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
pub fn close(self: &File) {
|
||||
os.close(self.handle);
|
||||
self.handle = undefined;
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than buf.len, then
|
||||
/// the stream reached End Of File.
|
||||
pub fn read(self: &InStream, buf: []u8) -> %usize {
|
||||
if (is_posix) {
|
||||
var index: usize = 0;
|
||||
while (index < buf.len) {
|
||||
const amt_read = system.read(self.fd, &buf[index], buf.len - index);
|
||||
const read_err = system.getErrno(amt_read);
|
||||
if (read_err > 0) {
|
||||
switch (read_err) {
|
||||
system.EINTR => continue,
|
||||
system.EINVAL => unreachable,
|
||||
system.EFAULT => unreachable,
|
||||
system.EBADF => return error.BadFd,
|
||||
system.EIO => return error.Io,
|
||||
else => return os.unexpectedErrorPosix(read_err),
|
||||
}
|
||||
}
|
||||
if (amt_read == 0) return index;
|
||||
index += amt_read;
|
||||
}
|
||||
return index;
|
||||
} else if (is_windows) {
|
||||
const handle = %return self.getHandle();
|
||||
var index: usize = 0;
|
||||
while (index < buf.len) {
|
||||
const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buf.len - index));
|
||||
var amt_read: system.DWORD = undefined;
|
||||
if (system.ReadFile(handle, @ptrCast(&c_void, &buf[index]), want_read_count, &amt_read, null) == 0) {
|
||||
const err = system.GetLastError();
|
||||
return switch (err) {
|
||||
system.ERROR.OPERATION_ABORTED => continue,
|
||||
system.ERROR.BROKEN_PIPE => return index,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
if (amt_read == 0) return index;
|
||||
index += amt_read;
|
||||
}
|
||||
return index;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
/// Calls `os.isTty` on `self.handle`.
|
||||
pub fn isTty(self: &File) -> bool {
|
||||
return os.isTty(self.handle);
|
||||
}
|
||||
|
||||
pub fn readNoEof(is: &InStream, buf: []u8) -> %void {
|
||||
const amt_read = %return is.read(buf);
|
||||
if (amt_read < buf.len) return error.EndOfFile;
|
||||
}
|
||||
|
||||
pub fn readByte(is: &InStream) -> %u8 {
|
||||
var result: [1]u8 = undefined;
|
||||
%return is.readNoEof(result[0..]);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
pub fn readByteSigned(is: &InStream) -> %i8 {
|
||||
var result: [1]i8 = undefined;
|
||||
%return is.readNoEof(([]u8)(result[0..]));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
pub fn readIntLe(is: &InStream, comptime T: type) -> %T {
|
||||
is.readInt(false, T)
|
||||
}
|
||||
|
||||
pub fn readIntBe(is: &InStream, comptime T: type) -> %T {
|
||||
is.readInt(true, T)
|
||||
}
|
||||
|
||||
pub fn readInt(is: &InStream, is_be: bool, comptime T: type) -> %T {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
%return is.readNoEof(bytes[0..]);
|
||||
return mem.readInt(bytes, T, is_be);
|
||||
}
|
||||
|
||||
pub fn readVarInt(is: &InStream, is_be: bool, comptime T: type, size: usize) -> %T {
|
||||
assert(size <= @sizeOf(T));
|
||||
assert(size <= 8);
|
||||
var input_buf: [8]u8 = undefined;
|
||||
const input_slice = input_buf[0..size];
|
||||
%return is.readNoEof(input_slice);
|
||||
return mem.readInt(input_slice, T, is_be);
|
||||
}
|
||||
|
||||
pub fn seekForward(is: &InStream, amount: isize) -> %void {
|
||||
pub fn seekForward(self: &File, amount: isize) -> %void {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin => {
|
||||
const result = system.lseek(is.fd, amount, system.SEEK_CUR);
|
||||
const result = system.lseek(self.handle, amount, system.SEEK_CUR);
|
||||
const err = system.getErrno(result);
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
|
@ -379,10 +177,10 @@ pub const InStream = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn seekTo(is: &InStream, pos: usize) -> %void {
|
||||
pub fn seekTo(self: &File, pos: usize) -> %void {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin => {
|
||||
const result = system.lseek(is.fd, @bitCast(isize, pos), system.SEEK_SET);
|
||||
const result = system.lseek(self.handle, @bitCast(isize, pos), system.SEEK_SET);
|
||||
const err = system.getErrno(result);
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
|
@ -399,10 +197,10 @@ pub const InStream = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn getPos(is: &InStream) -> %usize {
|
||||
pub fn getPos(self: &File) -> %usize {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin => {
|
||||
const result = system.lseek(is.fd, 0, system.SEEK_CUR);
|
||||
const result = system.lseek(self.handle, 0, system.SEEK_CUR);
|
||||
const err = system.getErrno(result);
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
|
@ -420,9 +218,9 @@ pub const InStream = struct {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn getEndPos(is: &InStream) -> %usize {
|
||||
pub fn getEndPos(self: &File) -> %usize {
|
||||
var stat: system.Stat = undefined;
|
||||
const err = system.getErrno(system.fstat(is.fd, &stat));
|
||||
const err = system.getErrno(system.fstat(self.handle, &stat));
|
||||
if (err > 0) {
|
||||
return switch (err) {
|
||||
system.EBADF => error.BadFd,
|
||||
|
@ -434,89 +232,217 @@ pub const InStream = struct {
|
|||
return usize(stat.size);
|
||||
}
|
||||
|
||||
pub fn readAll(is: &InStream, buf: &Buffer) -> %void {
|
||||
%return buf.resize(os.page_size);
|
||||
|
||||
var actual_buf_len: usize = 0;
|
||||
while (true) {
|
||||
const dest_slice = buf.toSlice()[actual_buf_len..];
|
||||
const bytes_read = %return is.read(dest_slice);
|
||||
actual_buf_len += bytes_read;
|
||||
|
||||
if (bytes_read != dest_slice.len) {
|
||||
return buf.resize(actual_buf_len);
|
||||
}
|
||||
|
||||
%return buf.resize(actual_buf_len + os.page_size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn readLine(is: &InStream, buf: &Buffer) -> %void {
|
||||
%return buf.resize(0);
|
||||
|
||||
while (true) {
|
||||
var byte: u8 = %return is.readByte();
|
||||
%return buf.appendByte(byte);
|
||||
|
||||
if (buf.endsWith(os.line_sep)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isTty(self: &InStream) -> %bool {
|
||||
fn readFn(in_stream: &InStream, buffer: []u8) -> %usize {
|
||||
const self = @fieldParentPtr(File, "in_stream", in_stream);
|
||||
if (is_posix) {
|
||||
if (builtin.link_libc) {
|
||||
return c.isatty(self.fd) != 0;
|
||||
} else {
|
||||
return system.isatty(self.fd);
|
||||
var index: usize = 0;
|
||||
while (index < buffer.len) {
|
||||
const amt_read = system.read(self.handle, &buffer[index], buffer.len - index);
|
||||
const read_err = system.getErrno(amt_read);
|
||||
if (read_err > 0) {
|
||||
switch (read_err) {
|
||||
system.EINTR => continue,
|
||||
system.EINVAL => unreachable,
|
||||
system.EFAULT => unreachable,
|
||||
system.EBADF => return error.BadFd,
|
||||
system.EIO => return error.Io,
|
||||
else => return os.unexpectedErrorPosix(read_err),
|
||||
}
|
||||
}
|
||||
if (amt_read == 0) return index;
|
||||
index += amt_read;
|
||||
}
|
||||
return index;
|
||||
} else if (is_windows) {
|
||||
return os.windowsIsTty(%return self.getHandle());
|
||||
var index: usize = 0;
|
||||
while (index < buffer.len) {
|
||||
const want_read_count = system.DWORD(math.min(system.DWORD(@maxValue(system.DWORD)), buffer.len - index));
|
||||
var amt_read: system.DWORD = undefined;
|
||||
if (system.ReadFile(self.handle, @ptrCast(&c_void, &buffer[index]), want_read_count, &amt_read, null) == 0) {
|
||||
const err = system.GetLastError();
|
||||
return switch (err) {
|
||||
system.ERROR.OPERATION_ABORTED => continue,
|
||||
system.ERROR.BROKEN_PIPE => return index,
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
if (amt_read == 0) return index;
|
||||
index += amt_read;
|
||||
}
|
||||
return index;
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
fn writeFn(out_stream: &OutStream, bytes: []const u8) -> %void {
|
||||
const self = @fieldParentPtr(File, "out_stream", out_stream);
|
||||
if (is_posix) {
|
||||
%return os.posixWrite(self.handle, bytes);
|
||||
} else if (is_windows) {
|
||||
%return os.windowsWrite(self.handle, bytes);
|
||||
} else {
|
||||
@compileError("Unsupported OS");
|
||||
}
|
||||
}
|
||||
|
||||
fn getHandle(self: &InStream) -> %system.HANDLE {
|
||||
if (self.handle) |handle| return handle;
|
||||
if (system.GetStdHandle(self.handle_id)) |handle| {
|
||||
if (handle == system.INVALID_HANDLE_VALUE) {
|
||||
const err = system.GetLastError();
|
||||
return switch (err) {
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
self.handle = handle;
|
||||
return handle;
|
||||
} else {
|
||||
return error.NoStdHandles;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn openSelfExe() -> %InStream {
|
||||
switch (builtin.os) {
|
||||
Os.linux => {
|
||||
return InStream.open("/proc/self/exe", null);
|
||||
},
|
||||
Os.darwin => {
|
||||
debug.panic("TODO: openSelfExe on Darwin");
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
/// `path` may need to be copied in memory to add a null terminating byte. In this case
|
||||
/// a fixed size buffer of size std.os.max_noalloc_path_len is an attempted solution. If the fixed
|
||||
/// size buffer is too small, and the provided allocator is null, error.NameTooLong is returned.
|
||||
/// a fixed size buffer of size `std.os.max_noalloc_path_len` is an attempted solution. If the fixed
|
||||
/// size buffer is too small, and the provided allocator is null, `error.NameTooLong` is returned.
|
||||
/// otherwise if the fixed size buffer is too small, allocator is used to obtain the needed memory.
|
||||
pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) -> %void {
|
||||
// TODO have an unbuffered File abstraction and use that here.
|
||||
// Then a buffered out stream abstraction can go on top of that for
|
||||
// use cases like stdout and stderr.
|
||||
var out_stream = %return OutStream.open(path, allocator);
|
||||
defer out_stream.close();
|
||||
%return out_stream.write(data);
|
||||
%return out_stream.flush();
|
||||
var file = %return File.openWrite(path, allocator);
|
||||
defer file.close();
|
||||
%return file.out_stream.write(data);
|
||||
}
|
||||
|
||||
error StreamTooLong;
|
||||
error EndOfStream;
|
||||
|
||||
pub const InStream = struct {
|
||||
/// Return the number of bytes read. If the number read is smaller than buf.len, it
|
||||
/// means the stream reached the end. Reaching the end of a stream is not an error
|
||||
/// condition.
|
||||
readFn: fn(self: &InStream, buffer: []u8) -> %usize,
|
||||
|
||||
/// Replaces `buffer` contents by reading from the stream until it is finished.
|
||||
/// If `buffer.len()` woould exceed `max_size`, `error.StreamTooLong` is returned and
|
||||
/// the contents read from the stream are lost.
|
||||
pub fn readAllBuffer(self: &InStream, buffer: &Buffer, max_size: usize) -> %void {
|
||||
%return buffer.resize(0);
|
||||
|
||||
var actual_buf_len: usize = 0;
|
||||
while (true) {
|
||||
const dest_slice = buffer.toSlice()[actual_buf_len..];
|
||||
const bytes_read = %return self.readFn(self, dest_slice);
|
||||
actual_buf_len += bytes_read;
|
||||
|
||||
if (bytes_read != dest_slice.len) {
|
||||
buffer.shrink(actual_buf_len);
|
||||
return;
|
||||
}
|
||||
|
||||
const new_buf_size = math.min(max_size, actual_buf_len + os.page_size);
|
||||
if (new_buf_size == actual_buf_len)
|
||||
return error.StreamTooLong;
|
||||
%return buffer.resize(new_buf_size);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates enough memory to hold all the contents of the stream. If the allocated
|
||||
/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
|
||||
/// Caller owns returned memory.
|
||||
/// If this function returns an error, the contents from the stream read so far are lost.
|
||||
pub fn readAllAlloc(self: &InStream, allocator: &mem.Allocator, max_size: usize) -> %[]u8 {
|
||||
var buf = Buffer.initNull(allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
%return self.readAllBuffer(self, &buf, max_size);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
/// Replaces `buffer` contents by reading from the stream until `delimiter` is found.
|
||||
/// Does not include the delimiter in the result.
|
||||
/// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
|
||||
/// read from the stream so far are lost.
|
||||
pub fn readUntilDelimiterBuffer(self: &InStream, buffer: &Buffer, delimiter: u8, max_size: usize) -> %void {
|
||||
%return buf.resize(0);
|
||||
|
||||
while (true) {
|
||||
var byte: u8 = %return self.readByte();
|
||||
|
||||
if (byte == delimiter) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf.len() == max_size) {
|
||||
return error.StreamTooLong;
|
||||
}
|
||||
|
||||
%return buf.appendByte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates enough memory to read until `delimiter`. If the allocated
|
||||
/// memory would be greater than `max_size`, returns `error.StreamTooLong`.
|
||||
/// Caller owns returned memory.
|
||||
/// If this function returns an error, the contents from the stream read so far are lost.
|
||||
pub fn readUntilDelimiterAlloc(self: &InStream, allocator: &mem.Allocator,
|
||||
delimiter: u8, max_size: usize) -> %[]u8
|
||||
{
|
||||
var buf = Buffer.initNull(allocator);
|
||||
defer buf.deinit();
|
||||
|
||||
%return self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
|
||||
return buf.toOwnedSlice();
|
||||
}
|
||||
|
||||
/// Returns the number of bytes read. If the number read is smaller than buf.len, it
|
||||
/// means the stream reached the end. Reaching the end of a stream is not an error
|
||||
/// condition.
|
||||
pub fn read(self: &InStream, buffer: []u8) -> %usize {
|
||||
return self.readFn(self, buffer);
|
||||
}
|
||||
|
||||
/// Same as `read` but end of stream returns `error.EndOfStream`.
|
||||
pub fn readNoEof(self: &InStream, buf: []u8) -> %void {
|
||||
const amt_read = %return self.read(buf);
|
||||
if (amt_read < buf.len) return error.EndOfStream;
|
||||
}
|
||||
|
||||
/// Reads 1 byte from the stream or returns `error.EndOfStream`.
|
||||
pub fn readByte(self: &InStream) -> %u8 {
|
||||
var result: [1]u8 = undefined;
|
||||
%return self.readNoEof(result[0..]);
|
||||
return result[0];
|
||||
}
|
||||
|
||||
/// Same as `readByte` except the returned byte is signed.
|
||||
pub fn readByteSigned(self: &InStream) -> %i8 {
|
||||
return @bitCast(i8, %return self.readByte());
|
||||
}
|
||||
|
||||
pub fn readIntLe(self: &InStream, comptime T: type) -> %T {
|
||||
return self.readInt(false, T);
|
||||
}
|
||||
|
||||
pub fn readIntBe(self: &InStream, comptime T: type) -> %T {
|
||||
return self.readInt(true, T);
|
||||
}
|
||||
|
||||
pub fn readInt(self: &InStream, is_be: bool, comptime T: type) -> %T {
|
||||
var bytes: [@sizeOf(T)]u8 = undefined;
|
||||
%return self.readNoEof(bytes[0..]);
|
||||
return mem.readInt(bytes, T, is_be);
|
||||
}
|
||||
|
||||
pub fn readVarInt(self: &InStream, is_be: bool, comptime T: type, size: usize) -> %T {
|
||||
assert(size <= @sizeOf(T));
|
||||
assert(size <= 8);
|
||||
var input_buf: [8]u8 = undefined;
|
||||
const input_slice = input_buf[0..size];
|
||||
%return self.readNoEof(input_slice);
|
||||
return mem.readInt(input_slice, T, is_be);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
pub const OutStream = struct {
|
||||
writeFn: fn(self: &OutStream, bytes: []const u8) -> %void,
|
||||
|
||||
pub fn print(self: &OutStream, comptime format: []const u8, args: ...) -> %void {
|
||||
return std.fmt.format(self, self.writeFn, format, args);
|
||||
}
|
||||
|
||||
pub fn write(self: &OutStream, bytes: []const u8) -> %void {
|
||||
return self.writeFn(self, bytes);
|
||||
}
|
||||
|
||||
pub fn writeByte(self: &OutStream, byte: u8) -> %void {
|
||||
const slice = (&byte)[0..1];
|
||||
return self.writeFn(self, slice);
|
||||
}
|
||||
};
|
||||
|
|
152
std/mem.zig
152
std/mem.zig
|
@ -1,16 +1,9 @@
|
|||
const debug = @import("debug.zig");
|
||||
const assert = debug.assert;
|
||||
const math = @import("math/index.zig");
|
||||
const os = @import("os/index.zig");
|
||||
const io = @import("io.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const c = @import("c/index.zig");
|
||||
|
||||
pub const Cmp = math.Cmp;
|
||||
|
||||
error OutOfMemory;
|
||||
|
||||
pub const Allocator = struct {
|
||||
/// Allocate byte_count bytes and return them in a slice, with the
|
||||
/// slicer's pointer aligned at least to alignment bytes.
|
||||
|
@ -85,151 +78,6 @@ pub const Allocator = struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub var c_allocator = Allocator {
|
||||
.allocFn = cAlloc,
|
||||
.reallocFn = cRealloc,
|
||||
.freeFn = cFree,
|
||||
};
|
||||
|
||||
fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||
if (c.malloc(usize(n))) |mem| {
|
||||
@ptrCast(&u8, mem)[0..n]
|
||||
} else {
|
||||
error.OutOfMemory
|
||||
}
|
||||
}
|
||||
|
||||
fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
old_mem[0..new_size]
|
||||
} else {
|
||||
const old_ptr = @ptrCast(&c_void, old_mem.ptr);
|
||||
if (c.realloc(old_ptr, usize(new_size))) |mem| {
|
||||
@ptrCast(&u8, mem)[0..new_size]
|
||||
} else {
|
||||
error.OutOfMemory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cFree(self: &Allocator, old_mem: []u8) {
|
||||
const old_ptr = @ptrCast(&c_void, old_mem.ptr);
|
||||
c.free(old_ptr);
|
||||
}
|
||||
|
||||
pub const IncrementingAllocator = struct {
|
||||
allocator: Allocator,
|
||||
bytes: []u8,
|
||||
end_index: usize,
|
||||
heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
|
||||
|
||||
fn init(capacity: usize) -> %IncrementingAllocator {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin, Os.macosx, Os.ios => {
|
||||
const p = os.posix;
|
||||
const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
|
||||
p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
|
||||
if (addr == p.MAP_FAILED) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
return IncrementingAllocator {
|
||||
.allocator = Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
.bytes = @intToPtr(&u8, addr)[0..capacity],
|
||||
.end_index = 0,
|
||||
.heap_handle = {},
|
||||
};
|
||||
},
|
||||
Os.windows => {
|
||||
const heap_handle = os.windows.GetProcessHeap() ?? return error.OutOfMemory;
|
||||
const ptr = os.windows.HeapAlloc(heap_handle, 0, capacity) ?? return error.OutOfMemory;
|
||||
return IncrementingAllocator {
|
||||
.allocator = Allocator {
|
||||
.allocFn = alloc,
|
||||
.reallocFn = realloc,
|
||||
.freeFn = free,
|
||||
},
|
||||
.bytes = @ptrCast(&u8, ptr)[0..capacity],
|
||||
.end_index = 0,
|
||||
.heap_handle = heap_handle,
|
||||
};
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
fn deinit(self: &IncrementingAllocator) {
|
||||
switch (builtin.os) {
|
||||
Os.linux, Os.darwin, Os.macosx, Os.ios => {
|
||||
_ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
|
||||
},
|
||||
Os.windows => {
|
||||
_ = os.windows.HeapFree(self.heap_handle, 0, @ptrCast(os.windows.LPVOID, self.bytes.ptr));
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(self: &IncrementingAllocator) {
|
||||
self.end_index = 0;
|
||||
}
|
||||
|
||||
fn bytesLeft(self: &const IncrementingAllocator) -> usize {
|
||||
return self.bytes.len - self.end_index;
|
||||
}
|
||||
|
||||
fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
|
||||
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
|
||||
const addr = @ptrToInt(&self.bytes[self.end_index]);
|
||||
const rem = @rem(addr, alignment);
|
||||
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
|
||||
const adjusted_index = self.end_index + march_forward_bytes;
|
||||
const new_end_index = adjusted_index + n;
|
||||
if (new_end_index > self.bytes.len) {
|
||||
return error.OutOfMemory;
|
||||
}
|
||||
const result = self.bytes[adjusted_index .. new_end_index];
|
||||
self.end_index = new_end_index;
|
||||
return result;
|
||||
}
|
||||
|
||||
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
|
||||
if (new_size <= old_mem.len) {
|
||||
return old_mem[0..new_size];
|
||||
} else {
|
||||
const result = %return alloc(allocator, new_size, alignment);
|
||||
copy(u8, result, old_mem);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
fn free(allocator: &Allocator, bytes: []u8) {
|
||||
// Do nothing. That's the point of an incrementing allocator.
|
||||
}
|
||||
};
|
||||
|
||||
test "mem.IncrementingAllocator" {
|
||||
const total_bytes = 100 * 1024 * 1024;
|
||||
var inc_allocator = %%IncrementingAllocator.init(total_bytes);
|
||||
defer inc_allocator.deinit();
|
||||
|
||||
const allocator = &inc_allocator.allocator;
|
||||
const slice = %%allocator.alloc(&i32, 100);
|
||||
|
||||
for (slice) |*item, i| {
|
||||
*item = %%allocator.create(i32);
|
||||
**item = i32(i);
|
||||
}
|
||||
|
||||
assert(inc_allocator.bytesLeft() == total_bytes - @sizeOf(i32) * 100 - @sizeOf(usize) * 100);
|
||||
|
||||
inc_allocator.reset();
|
||||
|
||||
assert(inc_allocator.bytesLeft() == total_bytes);
|
||||
}
|
||||
|
||||
/// Copy all of source into dest at position 0.
|
||||
/// dest.len must be >= source.len.
|
||||
|
|
|
@ -28,9 +28,9 @@ pub const ChildProcess = struct {
|
|||
|
||||
pub allocator: &mem.Allocator,
|
||||
|
||||
pub stdin: ?&io.OutStream,
|
||||
pub stdout: ?&io.InStream,
|
||||
pub stderr: ?&io.InStream,
|
||||
pub stdin: ?io.File,
|
||||
pub stdout: ?io.File,
|
||||
pub stderr: ?io.File,
|
||||
|
||||
pub term: ?%Term,
|
||||
|
||||
|
@ -250,17 +250,17 @@ pub const ChildProcess = struct {
|
|||
}
|
||||
|
||||
fn cleanupStreams(self: &ChildProcess) {
|
||||
if (self.stdin) |stdin| { stdin.close(); self.allocator.destroy(stdin); self.stdin = null; }
|
||||
if (self.stdout) |stdout| { stdout.close(); self.allocator.destroy(stdout); self.stdout = null; }
|
||||
if (self.stderr) |stderr| { stderr.close(); self.allocator.destroy(stderr); self.stderr = null; }
|
||||
if (self.stdin) |*stdin| { stdin.close(); self.stdin = null; }
|
||||
if (self.stdout) |*stdout| { stdout.close(); self.stdout = null; }
|
||||
if (self.stderr) |*stderr| { stderr.close(); self.stderr = null; }
|
||||
}
|
||||
|
||||
fn cleanupAfterWait(self: &ChildProcess, status: i32) -> %Term {
|
||||
children_nodes.remove(&self.llnode);
|
||||
|
||||
defer {
|
||||
os.posixClose(self.err_pipe[0]);
|
||||
os.posixClose(self.err_pipe[1]);
|
||||
os.close(self.err_pipe[0]);
|
||||
os.close(self.err_pipe[1]);
|
||||
};
|
||||
|
||||
// Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
|
||||
|
@ -310,7 +310,7 @@ pub const ChildProcess = struct {
|
|||
} else {
|
||||
undefined
|
||||
};
|
||||
defer { if (any_ignore) os.posixClose(dev_null_fd); };
|
||||
defer { if (any_ignore) os.close(dev_null_fd); };
|
||||
|
||||
var env_map_owned: BufMap = undefined;
|
||||
var we_own_env_map: bool = undefined;
|
||||
|
@ -329,27 +329,6 @@ pub const ChildProcess = struct {
|
|||
const err_pipe = %return makePipe();
|
||||
%defer destroyPipe(err_pipe);
|
||||
|
||||
const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.OutStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
%defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr);
|
||||
|
||||
const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.InStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
%defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr);
|
||||
|
||||
const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.InStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
%defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr);
|
||||
|
||||
block_SIGCHLD();
|
||||
const pid_result = posix.fork();
|
||||
const pid_err = posix.getErrno(pid_result);
|
||||
|
@ -390,46 +369,35 @@ pub const ChildProcess = struct {
|
|||
|
||||
// we are the parent
|
||||
const pid = i32(pid_result);
|
||||
if (stdin_ptr) |outstream| {
|
||||
*outstream = io.OutStream {
|
||||
.fd = stdin_pipe[1],
|
||||
.handle = {},
|
||||
.handle_id = {},
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
};
|
||||
if (self.stdin_behavior == StdIo.Pipe) {
|
||||
self.stdin = io.File.openHandle(stdin_pipe[1]);
|
||||
} else {
|
||||
self.stdin = null;
|
||||
}
|
||||
if (stdout_ptr) |instream| {
|
||||
*instream = io.InStream {
|
||||
.fd = stdout_pipe[0],
|
||||
.handle = {},
|
||||
.handle_id = {},
|
||||
};
|
||||
if (self.stdout_behavior == StdIo.Pipe) {
|
||||
self.stdout = io.File.openHandle(stdout_pipe[0]);
|
||||
} else {
|
||||
self.stdout = null;
|
||||
}
|
||||
if (stderr_ptr) |instream| {
|
||||
*instream = io.InStream {
|
||||
.fd = stderr_pipe[0],
|
||||
.handle = {},
|
||||
.handle_id = {},
|
||||
};
|
||||
if (self.stderr_behavior == StdIo.Pipe) {
|
||||
self.stderr = io.File.openHandle(stderr_pipe[0]);
|
||||
} else {
|
||||
self.stderr = null;
|
||||
}
|
||||
|
||||
self.pid = pid;
|
||||
self.err_pipe = err_pipe;
|
||||
self.llnode = LinkedList(&ChildProcess).Node.init(self);
|
||||
self.term = null;
|
||||
self.stdin = stdin_ptr;
|
||||
self.stdout = stdout_ptr;
|
||||
self.stderr = stderr_ptr;
|
||||
|
||||
// TODO make this atomic so it works even with threads
|
||||
children_nodes.prepend(&self.llnode);
|
||||
|
||||
restore_SIGCHLD();
|
||||
|
||||
if (self.stdin_behavior == StdIo.Pipe) { os.posixClose(stdin_pipe[0]); }
|
||||
if (self.stdout_behavior == StdIo.Pipe) { os.posixClose(stdout_pipe[1]); }
|
||||
if (self.stderr_behavior == StdIo.Pipe) { os.posixClose(stderr_pipe[1]); }
|
||||
if (self.stdin_behavior == StdIo.Pipe) { os.close(stdin_pipe[0]); }
|
||||
if (self.stdout_behavior == StdIo.Pipe) { os.close(stdout_pipe[1]); }
|
||||
if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
|
||||
}
|
||||
|
||||
fn spawnWindows(self: &ChildProcess) -> %void {
|
||||
|
@ -509,27 +477,6 @@ pub const ChildProcess = struct {
|
|||
}
|
||||
%defer if (self.stdin_behavior == StdIo.Pipe) { windowsDestroyPipe(g_hChildStd_ERR_Rd, g_hChildStd_ERR_Wr); };
|
||||
|
||||
const stdin_ptr = if (self.stdin_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.OutStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
%defer if (stdin_ptr) |ptr| self.allocator.destroy(ptr);
|
||||
|
||||
const stdout_ptr = if (self.stdout_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.InStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
%defer if (stdout_ptr) |ptr| self.allocator.destroy(ptr);
|
||||
|
||||
const stderr_ptr = if (self.stderr_behavior == StdIo.Pipe) {
|
||||
%return self.allocator.create(io.InStream)
|
||||
} else {
|
||||
null
|
||||
};
|
||||
%defer if (stderr_ptr) |ptr| self.allocator.destroy(ptr);
|
||||
|
||||
const cmd_line = %return windowsCreateCommandLine(self.allocator, self.argv);
|
||||
defer self.allocator.free(cmd_line);
|
||||
|
||||
|
@ -609,36 +556,25 @@ pub const ChildProcess = struct {
|
|||
}
|
||||
};
|
||||
|
||||
if (stdin_ptr) |outstream| {
|
||||
*outstream = io.OutStream {
|
||||
.fd = {},
|
||||
.handle = g_hChildStd_IN_Wr,
|
||||
.handle_id = undefined,
|
||||
.buffer = undefined,
|
||||
.index = 0,
|
||||
};
|
||||
if (self.stdin_behavior == StdIo.Pipe) {
|
||||
self.stdin = io.File.openHandle(g_hChildStd_IN_Wr);
|
||||
} else {
|
||||
self.stdin = null;
|
||||
}
|
||||
if (stdout_ptr) |instream| {
|
||||
*instream = io.InStream {
|
||||
.fd = {},
|
||||
.handle = g_hChildStd_OUT_Rd,
|
||||
.handle_id = undefined,
|
||||
};
|
||||
if (self.stdout_behavior == StdIo.Pipe) {
|
||||
self.stdout = io.File.openHandle(g_hChildStd_OUT_Rd);
|
||||
} else {
|
||||
self.stdout = null;
|
||||
}
|
||||
if (stderr_ptr) |instream| {
|
||||
*instream = io.InStream {
|
||||
.fd = {},
|
||||
.handle = g_hChildStd_ERR_Rd,
|
||||
.handle_id = undefined,
|
||||
};
|
||||
if (self.stderr_behavior == StdIo.Pipe) {
|
||||
self.stderr = io.File.openHandle(g_hChildStd_ERR_Rd);
|
||||
} else {
|
||||
self.stderr = null;
|
||||
}
|
||||
|
||||
self.handle = piProcInfo.hProcess;
|
||||
self.thread_handle = piProcInfo.hThread;
|
||||
self.term = null;
|
||||
self.stdin = stdin_ptr;
|
||||
self.stdout = stdout_ptr;
|
||||
self.stderr = stderr_ptr;
|
||||
|
||||
if (self.stdin_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_IN_Rd); }
|
||||
if (self.stderr_behavior == StdIo.Pipe) { os.windowsClose(??g_hChildStd_ERR_Wr); }
|
||||
|
@ -648,7 +584,7 @@ pub const ChildProcess = struct {
|
|||
fn setUpChildIo(stdio: StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) -> %void {
|
||||
switch (stdio) {
|
||||
StdIo.Pipe => %return os.posixDup2(pipe_fd, std_fileno),
|
||||
StdIo.Close => os.posixClose(std_fileno),
|
||||
StdIo.Close => os.close(std_fileno),
|
||||
StdIo.Inherit => {},
|
||||
StdIo.Ignore => %return os.posixDup2(dev_null_fd, std_fileno),
|
||||
}
|
||||
|
@ -771,8 +707,8 @@ fn makePipe() -> %[2]i32 {
|
|||
}
|
||||
|
||||
fn destroyPipe(pipe: &const [2]i32) {
|
||||
os.posixClose((*pipe)[0]);
|
||||
os.posixClose((*pipe)[1]);
|
||||
os.close((*pipe)[0]);
|
||||
os.close((*pipe)[1]);
|
||||
}
|
||||
|
||||
// Child of fork calls this to report an error to the fork parent.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const builtin = @import("builtin");
|
||||
const Os = builtin.Os;
|
||||
const is_windows = builtin.os == Os.windows;
|
||||
const os = this;
|
||||
|
||||
pub const windows = @import("windows/index.zig");
|
||||
pub const darwin = @import("darwin.zig");
|
||||
|
@ -26,14 +27,14 @@ pub const UserInfo = @import("get_user_id.zig").UserInfo;
|
|||
pub const getUserInfo = @import("get_user_id.zig").getUserInfo;
|
||||
|
||||
const windows_util = @import("windows/util.zig");
|
||||
pub const windowsClose = windows_util.windowsClose;
|
||||
pub const windowsWaitSingle = windows_util.windowsWaitSingle;
|
||||
pub const windowsWrite = windows_util.windowsWrite;
|
||||
pub const windowsIsTty = windows_util.windowsIsTty;
|
||||
pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
|
||||
pub const windowsOpen = windows_util.windowsOpen;
|
||||
pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
|
||||
|
||||
pub const FileHandle = if (is_windows) windows.HANDLE else i32;
|
||||
|
||||
const debug = @import("../debug.zig");
|
||||
const assert = debug.assert;
|
||||
|
||||
|
@ -88,7 +89,7 @@ pub fn getRandomBytes(buf: []u8) -> %void {
|
|||
Os.darwin, Os.macosx, Os.ios => {
|
||||
const fd = %return posixOpen("/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC,
|
||||
0, null);
|
||||
defer posixClose(fd);
|
||||
defer close(fd);
|
||||
|
||||
%return posixRead(fd, buf);
|
||||
},
|
||||
|
@ -165,14 +166,18 @@ pub coldcc fn exit(status: i32) -> noreturn {
|
|||
}
|
||||
}
|
||||
|
||||
/// Calls POSIX close, and keeps trying if it gets interrupted.
|
||||
pub fn posixClose(fd: i32) {
|
||||
while (true) {
|
||||
const err = posix.getErrno(posix.close(fd));
|
||||
if (err == posix.EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
/// Closes the file handle. Keeps trying if it gets interrupted by a signal.
|
||||
pub fn close(handle: FileHandle) {
|
||||
if (is_windows) {
|
||||
windows_util.windowsClose(handle);
|
||||
} else {
|
||||
while (true) {
|
||||
const err = posix.getErrno(posix.close(handle));
|
||||
if (err == posix.EINTR) {
|
||||
continue;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -716,19 +721,18 @@ pub fn copyFileMode(allocator: &Allocator, source_path: []const u8, dest_path: [
|
|||
%return getRandomBytes(rand_buf[0..]);
|
||||
_ = base64.encodeWithAlphabet(tmp_path[dest_path.len..], rand_buf, b64_fs_alphabet);
|
||||
|
||||
var out_stream = %return io.OutStream.openMode(tmp_path, mode, allocator);
|
||||
defer out_stream.close();
|
||||
var out_file = %return io.File.openWriteMode(tmp_path, mode, allocator);
|
||||
defer out_file.close();
|
||||
%defer _ = deleteFile(allocator, tmp_path);
|
||||
|
||||
var in_stream = %return io.InStream.open(source_path, allocator);
|
||||
defer in_stream.close();
|
||||
var in_file = %return io.File.openRead(source_path, allocator);
|
||||
defer in_file.close();
|
||||
|
||||
const buf = out_stream.buffer[0..];
|
||||
var buf: [page_size]u8 = undefined;
|
||||
while (true) {
|
||||
const amt = %return in_stream.read(buf);
|
||||
out_stream.index = amt;
|
||||
%return out_stream.flush();
|
||||
if (amt != out_stream.buffer.len)
|
||||
const amt = %return in_file.in_stream.read(buf[0..]);
|
||||
%return out_file.out_stream.write(buf[0..amt]);
|
||||
if (amt != buf.len)
|
||||
return rename(allocator, tmp_path, dest_path);
|
||||
}
|
||||
}
|
||||
|
@ -973,7 +977,7 @@ pub const Dir = struct {
|
|||
|
||||
pub fn close(self: &Dir) {
|
||||
self.allocator.free(self.buf);
|
||||
posixClose(self.fd);
|
||||
close(self.fd);
|
||||
}
|
||||
|
||||
/// Memory such as file names referenced in this returned entry becomes invalid
|
||||
|
@ -1135,7 +1139,6 @@ test "os.sleep" {
|
|||
sleep(0, 1);
|
||||
}
|
||||
|
||||
|
||||
error ResourceLimitReached;
|
||||
error InvalidUserId;
|
||||
error PermissionDenied;
|
||||
|
@ -1184,6 +1187,21 @@ pub fn posix_setregid(rgid: u32, egid: u32) -> %void {
|
|||
};
|
||||
}
|
||||
|
||||
error NoStdHandles;
|
||||
pub fn windowsGetStdHandle(handle_id: windows.DWORD) -> %windows.HANDLE {
|
||||
if (windows.GetStdHandle(handle_id)) |handle| {
|
||||
if (handle == windows.INVALID_HANDLE_VALUE) {
|
||||
const err = windows.GetLastError();
|
||||
return switch (err) {
|
||||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
return handle;
|
||||
} else {
|
||||
return error.NoStdHandles;
|
||||
}
|
||||
}
|
||||
|
||||
pub const ArgIteratorPosix = struct {
|
||||
index: usize,
|
||||
count: usize,
|
||||
|
@ -1458,3 +1476,28 @@ pub fn unexpectedErrorWindows(err: windows.DWORD) -> error {
|
|||
}
|
||||
return error.Unexpected;
|
||||
}
|
||||
|
||||
pub fn openSelfExe() -> %io.File {
|
||||
switch (builtin.os) {
|
||||
Os.linux => {
|
||||
return io.File.openRead("/proc/self/exe", null);
|
||||
},
|
||||
Os.darwin => {
|
||||
@panic("TODO: openSelfExe on Darwin");
|
||||
},
|
||||
else => @compileError("Unsupported OS"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isTty(handle: FileHandle) -> bool {
|
||||
if (is_windows) {
|
||||
return windows_util.windowsIsTty(handle);
|
||||
} else {
|
||||
if (builtin.link_libc) {
|
||||
return c.isatty(handle) != 0;
|
||||
} else {
|
||||
return posix.isatty(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -940,7 +940,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
|||
else => os.unexpectedErrorWindows(err),
|
||||
};
|
||||
}
|
||||
defer os.windowsClose(h_file);
|
||||
defer os.close(h_file);
|
||||
var buf = %return allocator.alloc(u8, 256);
|
||||
%defer allocator.free(buf);
|
||||
while (true) {
|
||||
|
@ -1009,7 +1009,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
|
|||
},
|
||||
Os.linux => {
|
||||
const fd = %return os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator);
|
||||
defer os.posixClose(fd);
|
||||
defer os.close(fd);
|
||||
|
||||
var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
|
||||
const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd);
|
||||
|
|
|
@ -6,6 +6,7 @@ const os = std.os;
|
|||
const Builder = std.build.Builder;
|
||||
const mem = std.mem;
|
||||
const ArrayList = std.ArrayList;
|
||||
const warn = std.debug.warn;
|
||||
|
||||
error InvalidArgs;
|
||||
|
||||
|
@ -13,7 +14,7 @@ pub fn main() -> %void {
|
|||
var arg_it = os.args();
|
||||
|
||||
// TODO use a more general purpose allocator here
|
||||
var inc_allocator = %%mem.IncrementingAllocator.init(20 * 1024 * 1024);
|
||||
var inc_allocator = %%std.heap.IncrementingAllocator.init(20 * 1024 * 1024);
|
||||
defer inc_allocator.deinit();
|
||||
|
||||
const allocator = &inc_allocator.allocator;
|
||||
|
@ -23,15 +24,15 @@ pub fn main() -> %void {
|
|||
_ = arg_it.skip();
|
||||
|
||||
const zig_exe = %return unwrapArg(arg_it.next(allocator) ?? {
|
||||
%%io.stderr.printf("Expected first argument to be path to zig compiler\n");
|
||||
warn("Expected first argument to be path to zig compiler\n");
|
||||
return error.InvalidArgs;
|
||||
});
|
||||
const build_root = %return unwrapArg(arg_it.next(allocator) ?? {
|
||||
%%io.stderr.printf("Expected second argument to be build root directory path\n");
|
||||
warn("Expected second argument to be build root directory path\n");
|
||||
return error.InvalidArgs;
|
||||
});
|
||||
const cache_root = %return unwrapArg(arg_it.next(allocator) ?? {
|
||||
%%io.stderr.printf("Expected third argument to be cache root directory path\n");
|
||||
warn("Expected third argument to be cache root directory path\n");
|
||||
return error.InvalidArgs;
|
||||
});
|
||||
|
||||
|
@ -42,32 +43,37 @@ pub fn main() -> %void {
|
|||
|
||||
var prefix: ?[]const u8 = null;
|
||||
|
||||
var stderr_file = io.getStdErr();
|
||||
var stderr_stream: %&io.OutStream = if (stderr_file) |*f| &f.out_stream else |err| err;
|
||||
var stdout_file = io.getStdOut();
|
||||
var stdout_stream: %&io.OutStream = if (stdout_file) |*f| &f.out_stream else |err| err;
|
||||
|
||||
while (arg_it.next(allocator)) |err_or_arg| {
|
||||
const arg = %return unwrapArg(err_or_arg);
|
||||
if (mem.startsWith(u8, arg, "-D")) {
|
||||
const option_contents = arg[2..];
|
||||
if (option_contents.len == 0) {
|
||||
%%io.stderr.printf("Expected option name after '-D'\n\n");
|
||||
return usage(&builder, false, &io.stderr);
|
||||
warn("Expected option name after '-D'\n\n");
|
||||
return usageAndErr(&builder, false, %return stderr_stream);
|
||||
}
|
||||
if (mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
|
||||
const option_name = option_contents[0..name_end];
|
||||
const option_value = option_contents[name_end + 1..];
|
||||
if (builder.addUserInputOption(option_name, option_value))
|
||||
return usage(&builder, false, &io.stderr);
|
||||
return usageAndErr(&builder, false, %return stderr_stream);
|
||||
} else {
|
||||
if (builder.addUserInputFlag(option_contents))
|
||||
return usage(&builder, false, &io.stderr);
|
||||
return usageAndErr(&builder, false, %return stderr_stream);
|
||||
}
|
||||
} else if (mem.startsWith(u8, arg, "-")) {
|
||||
if (mem.eql(u8, arg, "--verbose")) {
|
||||
builder.verbose = true;
|
||||
} else if (mem.eql(u8, arg, "--help")) {
|
||||
return usage(&builder, false, &io.stdout);
|
||||
return usage(&builder, false, %return stdout_stream);
|
||||
} else if (mem.eql(u8, arg, "--prefix")) {
|
||||
prefix = %return unwrapArg(arg_it.next(allocator) ?? {
|
||||
%%io.stderr.printf("Expected argument after --prefix\n\n");
|
||||
return usage(&builder, false, &io.stderr);
|
||||
warn("Expected argument after --prefix\n\n");
|
||||
return usageAndErr(&builder, false, %return stderr_stream);
|
||||
});
|
||||
} else if (mem.eql(u8, arg, "--verbose-tokenize")) {
|
||||
builder.verbose_tokenize = true;
|
||||
|
@ -82,8 +88,8 @@ pub fn main() -> %void {
|
|||
} else if (mem.eql(u8, arg, "--verbose-cimport")) {
|
||||
builder.verbose_cimport = true;
|
||||
} else {
|
||||
%%io.stderr.printf("Unrecognized argument: {}\n\n", arg);
|
||||
return usage(&builder, false, &io.stderr);
|
||||
warn("Unrecognized argument: {}\n\n", arg);
|
||||
return usageAndErr(&builder, false, %return stderr_stream);
|
||||
}
|
||||
} else {
|
||||
%%targets.append(arg);
|
||||
|
@ -94,11 +100,11 @@ pub fn main() -> %void {
|
|||
root.build(&builder);
|
||||
|
||||
if (builder.validateUserInputDidItFail())
|
||||
return usage(&builder, true, &io.stderr);
|
||||
return usageAndErr(&builder, true, %return stderr_stream);
|
||||
|
||||
builder.make(targets.toSliceConst()) %% |err| {
|
||||
if (err == error.InvalidStepName) {
|
||||
return usage(&builder, true, &io.stderr);
|
||||
return usageAndErr(&builder, true, %return stderr_stream);
|
||||
}
|
||||
return err;
|
||||
};
|
||||
|
@ -112,7 +118,7 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
|
|||
}
|
||||
|
||||
// This usage text has to be synchronized with src/main.cpp
|
||||
%%out_stream.printf(
|
||||
%return out_stream.print(
|
||||
\\Usage: {} build [steps] [options]
|
||||
\\
|
||||
\\Steps:
|
||||
|
@ -121,10 +127,10 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
|
|||
|
||||
const allocator = builder.allocator;
|
||||
for (builder.top_level_steps.toSliceConst()) |top_level_step| {
|
||||
%%out_stream.printf(" {s22} {}\n", top_level_step.step.name, top_level_step.description);
|
||||
%return out_stream.print(" {s22} {}\n", top_level_step.step.name, top_level_step.description);
|
||||
}
|
||||
|
||||
%%out_stream.write(
|
||||
%return out_stream.write(
|
||||
\\
|
||||
\\General Options:
|
||||
\\ --help Print this help and exit
|
||||
|
@ -136,17 +142,17 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
|
|||
);
|
||||
|
||||
if (builder.available_options_list.len == 0) {
|
||||
%%out_stream.print(" (none)\n");
|
||||
%return out_stream.print(" (none)\n");
|
||||
} else {
|
||||
for (builder.available_options_list.toSliceConst()) |option| {
|
||||
const name = %%fmt.allocPrint(allocator,
|
||||
const name = %return fmt.allocPrint(allocator,
|
||||
" -D{}=${}", option.name, Builder.typeIdName(option.type_id));
|
||||
defer allocator.free(name);
|
||||
%%out_stream.print("{s24} {}\n", name, option.description);
|
||||
%return out_stream.print("{s24} {}\n", name, option.description);
|
||||
}
|
||||
}
|
||||
|
||||
%%out_stream.write(
|
||||
%return out_stream.write(
|
||||
\\
|
||||
\\Advanced Options:
|
||||
\\ --build-file $file Override path to build.zig
|
||||
|
@ -159,16 +165,16 @@ fn usage(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream)
|
|||
\\ --verbose-cimport Enable compiler debug output for C imports
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
%%out_stream.flush();
|
||||
|
||||
if (out_stream == &io.stderr)
|
||||
return error.InvalidArgs;
|
||||
fn usageAndErr(builder: &Builder, already_ran_build: bool, out_stream: &io.OutStream) -> error {
|
||||
usage(builder, already_ran_build, out_stream) %% {};
|
||||
return error.InvalidArgs;
|
||||
}
|
||||
|
||||
fn unwrapArg(arg: %[]u8) -> %[]u8 {
|
||||
return arg %% |err| {
|
||||
%%io.stderr.printf("Unable to parse command line: {}\n", err);
|
||||
warn("Unable to parse command line: {}\n", err);
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
const io = @import("std").io;
|
||||
const std = @import("std");
|
||||
const io = std.io;
|
||||
const builtin = @import("builtin");
|
||||
const test_fn_list = builtin.__zig_test_fn_slice;
|
||||
const warn = std.debug.warn;
|
||||
|
||||
pub fn main() -> %void {
|
||||
for (test_fn_list) |test_fn, i| {
|
||||
%%io.stderr.printf("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
|
||||
warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
|
||||
|
||||
test_fn.func();
|
||||
|
||||
%%io.stderr.printf("OK\n");
|
||||
warn("OK\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\
|
||||
\\pub fn main() -> %void {
|
||||
\\ privateFunction();
|
||||
\\ %%stdout.printf("OK 2\n");
|
||||
\\ const stdout = &(%%getStdOut()).out_stream;
|
||||
\\ %%stdout.print("OK 2\n");
|
||||
\\}
|
||||
\\
|
||||
\\fn privateFunction() {
|
||||
|
@ -31,7 +32,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\// purposefully conflicting function with main.zig
|
||||
\\// but it's private so it should be OK
|
||||
\\fn privateFunction() {
|
||||
\\ %%stdout.printf("OK 1\n");
|
||||
\\ const stdout = &(%%getStdOut()).out_stream;
|
||||
\\ %%stdout.print("OK 1\n");
|
||||
\\}
|
||||
\\
|
||||
\\pub fn printText() {
|
||||
|
@ -56,7 +58,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
tc.addSourceFile("foo.zig",
|
||||
\\use @import("std").io;
|
||||
\\pub fn foo_function() {
|
||||
\\ %%stdout.printf("OK\n");
|
||||
\\ const stdout = &(%%getStdOut()).out_stream;
|
||||
\\ %%stdout.print("OK\n");
|
||||
\\}
|
||||
);
|
||||
|
||||
|
@ -66,7 +69,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\
|
||||
\\pub fn bar_function() {
|
||||
\\ if (foo_function()) {
|
||||
\\ %%stdout.printf("OK\n");
|
||||
\\ const stdout = &(%%getStdOut()).out_stream;
|
||||
\\ %%stdout.print("OK\n");
|
||||
\\ }
|
||||
\\}
|
||||
);
|
||||
|
@ -97,7 +101,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\pub const a_text = "OK\n";
|
||||
\\
|
||||
\\pub fn ok() {
|
||||
\\ %%io.stdout.printf(b_text);
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print(b_text);
|
||||
\\}
|
||||
);
|
||||
|
||||
|
@ -114,7 +119,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\const io = @import("std").io;
|
||||
\\
|
||||
\\pub fn main() -> %void {
|
||||
\\ %%io.stdout.printf("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print("Hello, world!\n{d4} {x3} {c}\n", u32(12), u16(0x12), u8('a'));
|
||||
\\}
|
||||
, "Hello, world!\n0012 012 a\n");
|
||||
|
||||
|
@ -266,7 +272,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\ var x_local : i32 = print_ok(x);
|
||||
\\}
|
||||
\\fn print_ok(val: @typeOf(x)) -> @typeOf(foo) {
|
||||
\\ %%io.stdout.printf("OK\n");
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print("OK\n");
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\const foo : i32 = 0;
|
||||
|
@ -347,24 +354,26 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\pub fn main() -> %void {
|
||||
\\ const bar = Bar {.field2 = 13,};
|
||||
\\ const foo = Foo {.field1 = bar,};
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ if (!foo.method()) {
|
||||
\\ %%io.stdout.printf("BAD\n");
|
||||
\\ %%stdout.print("BAD\n");
|
||||
\\ }
|
||||
\\ if (!bar.method()) {
|
||||
\\ %%io.stdout.printf("BAD\n");
|
||||
\\ %%stdout.print("BAD\n");
|
||||
\\ }
|
||||
\\ %%io.stdout.printf("OK\n");
|
||||
\\ %%stdout.print("OK\n");
|
||||
\\}
|
||||
, "OK\n");
|
||||
|
||||
cases.add("defer with only fallthrough",
|
||||
\\const io = @import("std").io;
|
||||
\\pub fn main() -> %void {
|
||||
\\ %%io.stdout.printf("before\n");
|
||||
\\ defer %%io.stdout.printf("defer1\n");
|
||||
\\ defer %%io.stdout.printf("defer2\n");
|
||||
\\ defer %%io.stdout.printf("defer3\n");
|
||||
\\ %%io.stdout.printf("after\n");
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print("before\n");
|
||||
\\ defer %%stdout.print("defer1\n");
|
||||
\\ defer %%stdout.print("defer2\n");
|
||||
\\ defer %%stdout.print("defer3\n");
|
||||
\\ %%stdout.print("after\n");
|
||||
\\}
|
||||
, "before\nafter\ndefer3\ndefer2\ndefer1\n");
|
||||
|
||||
|
@ -372,13 +381,14 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\const io = @import("std").io;
|
||||
\\const os = @import("std").os;
|
||||
\\pub fn main() -> %void {
|
||||
\\ %%io.stdout.printf("before\n");
|
||||
\\ defer %%io.stdout.printf("defer1\n");
|
||||
\\ defer %%io.stdout.printf("defer2\n");
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print("before\n");
|
||||
\\ defer %%stdout.print("defer1\n");
|
||||
\\ defer %%stdout.print("defer2\n");
|
||||
\\ var args_it = @import("std").os.args();
|
||||
\\ if (args_it.skip() and !args_it.skip()) return;
|
||||
\\ defer %%io.stdout.printf("defer3\n");
|
||||
\\ %%io.stdout.printf("after\n");
|
||||
\\ defer %%stdout.print("defer3\n");
|
||||
\\ %%stdout.print("after\n");
|
||||
\\}
|
||||
, "before\ndefer2\ndefer1\n");
|
||||
|
||||
|
@ -388,12 +398,13 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\ do_test() %% return;
|
||||
\\}
|
||||
\\fn do_test() -> %void {
|
||||
\\ %%io.stdout.printf("before\n");
|
||||
\\ defer %%io.stdout.printf("defer1\n");
|
||||
\\ %defer %%io.stdout.printf("deferErr\n");
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print("before\n");
|
||||
\\ defer %%stdout.print("defer1\n");
|
||||
\\ %defer %%stdout.print("deferErr\n");
|
||||
\\ %return its_gonna_fail();
|
||||
\\ defer %%io.stdout.printf("defer3\n");
|
||||
\\ %%io.stdout.printf("after\n");
|
||||
\\ defer %%stdout.print("defer3\n");
|
||||
\\ %%stdout.print("after\n");
|
||||
\\}
|
||||
\\error IToldYouItWouldFail;
|
||||
\\fn its_gonna_fail() -> %void {
|
||||
|
@ -407,12 +418,13 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\ do_test() %% return;
|
||||
\\}
|
||||
\\fn do_test() -> %void {
|
||||
\\ %%io.stdout.printf("before\n");
|
||||
\\ defer %%io.stdout.printf("defer1\n");
|
||||
\\ %defer %%io.stdout.printf("deferErr\n");
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print("before\n");
|
||||
\\ defer %%stdout.print("defer1\n");
|
||||
\\ %defer %%stdout.print("deferErr\n");
|
||||
\\ %return its_gonna_pass();
|
||||
\\ defer %%io.stdout.printf("defer3\n");
|
||||
\\ %%io.stdout.printf("after\n");
|
||||
\\ defer %%stdout.print("defer3\n");
|
||||
\\ %%stdout.print("after\n");
|
||||
\\}
|
||||
\\fn its_gonna_pass() -> %void { }
|
||||
, "before\nafter\ndefer3\ndefer1\n");
|
||||
|
@ -423,7 +435,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
|
|||
\\const io = @import("std").io;
|
||||
\\
|
||||
\\pub fn main() -> %void {
|
||||
\\ %%io.stdout.printf(foo_txt);
|
||||
\\ const stdout = &(%%io.getStdOut()).out_stream;
|
||||
\\ %%stdout.print(foo_txt);
|
||||
\\}
|
||||
, "1234\nabcd\n");
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const std = @import("std");
|
||||
const debug = std.debug;
|
||||
const warn = debug.warn;
|
||||
const build = std.build;
|
||||
const os = std.os;
|
||||
const StdIo = os.ChildProcess.StdIo;
|
||||
|
@ -50,6 +51,8 @@ const test_targets = []TestTarget {
|
|||
|
||||
error TestFailed;
|
||||
|
||||
const max_stdout_size = 1 * 1024 * 1024; // 1 MB
|
||||
|
||||
pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
|
||||
const cases = %%b.allocator.create(CompareOutputContext);
|
||||
*cases = CompareOutputContext {
|
||||
|
@ -231,7 +234,7 @@ pub const CompareOutputContext = struct {
|
|||
|
||||
const full_exe_path = b.pathFromRoot(self.exe_path);
|
||||
|
||||
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
|
||||
const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
|
||||
defer child.deinit();
|
||||
|
@ -246,8 +249,8 @@ pub const CompareOutputContext = struct {
|
|||
var stdout = Buffer.initNull(b.allocator);
|
||||
var stderr = Buffer.initNull(b.allocator);
|
||||
|
||||
%%(??child.stdout).readAll(&stdout);
|
||||
%%(??child.stderr).readAll(&stderr);
|
||||
%%(??child.stdout).in_stream.readAllBuffer(&stdout, max_stdout_size);
|
||||
%%(??child.stderr).in_stream.readAllBuffer(&stderr, max_stdout_size);
|
||||
|
||||
const term = child.wait() %% |err| {
|
||||
debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
|
||||
|
@ -255,19 +258,19 @@ pub const CompareOutputContext = struct {
|
|||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
if (code != 0) {
|
||||
%%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
|
||||
warn("Process {} exited with error code {}\n", full_exe_path, code);
|
||||
return error.TestFailed;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
%%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
|
||||
warn("Process {} terminated unexpectedly\n", full_exe_path);
|
||||
return error.TestFailed;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
|
||||
%%io.stderr.printf(
|
||||
warn(
|
||||
\\
|
||||
\\========= Expected this output: =========
|
||||
\\{}
|
||||
|
@ -277,7 +280,7 @@ pub const CompareOutputContext = struct {
|
|||
, self.expected_output, stdout.toSliceConst());
|
||||
return error.TestFailed;
|
||||
}
|
||||
%%io.stderr.printf("OK\n");
|
||||
warn("OK\n");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -310,7 +313,7 @@ pub const CompareOutputContext = struct {
|
|||
|
||||
const full_exe_path = b.pathFromRoot(self.exe_path);
|
||||
|
||||
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
|
||||
const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
|
||||
defer child.deinit();
|
||||
|
@ -328,24 +331,24 @@ pub const CompareOutputContext = struct {
|
|||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
if (code != expected_exit_code) {
|
||||
%%io.stderr.printf("\nProgram expected to exit with code {} " ++
|
||||
warn("\nProgram expected to exit with code {} " ++
|
||||
"but exited with code {}\n", expected_exit_code, code);
|
||||
return error.TestFailed;
|
||||
}
|
||||
},
|
||||
Term.Signal => |sig| {
|
||||
%%io.stderr.printf("\nProgram expected to exit with code {} " ++
|
||||
warn("\nProgram expected to exit with code {} " ++
|
||||
"but instead signaled {}\n", expected_exit_code, sig);
|
||||
return error.TestFailed;
|
||||
},
|
||||
else => {
|
||||
%%io.stderr.printf("\nProgram expected to exit with code {}" ++
|
||||
warn("\nProgram expected to exit with code {}" ++
|
||||
" but exited in an unexpected way\n", expected_exit_code);
|
||||
return error.TestFailed;
|
||||
},
|
||||
}
|
||||
|
||||
%%io.stderr.printf("OK\n");
|
||||
warn("OK\n");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -554,7 +557,7 @@ pub const CompileErrorContext = struct {
|
|||
Mode.ReleaseFast => %%zig_args.append("--release-fast"),
|
||||
}
|
||||
|
||||
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
|
||||
if (b.verbose) {
|
||||
printInvocation(zig_args.toSliceConst());
|
||||
|
@ -573,8 +576,8 @@ pub const CompileErrorContext = struct {
|
|||
var stdout_buf = Buffer.initNull(b.allocator);
|
||||
var stderr_buf = Buffer.initNull(b.allocator);
|
||||
|
||||
%%(??child.stdout).readAll(&stdout_buf);
|
||||
%%(??child.stderr).readAll(&stderr_buf);
|
||||
%%(??child.stdout).in_stream.readAllBuffer(&stdout_buf, max_stdout_size);
|
||||
%%(??child.stderr).in_stream.readAllBuffer(&stderr_buf, max_stdout_size);
|
||||
|
||||
const term = child.wait() %% |err| {
|
||||
debug.panic("Unable to spawn {}: {}\n", zig_args.items[0], @errorName(err));
|
||||
|
@ -582,12 +585,12 @@ pub const CompileErrorContext = struct {
|
|||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
if (code == 0) {
|
||||
%%io.stderr.printf("Compilation incorrectly succeeded\n");
|
||||
warn("Compilation incorrectly succeeded\n");
|
||||
return error.TestFailed;
|
||||
}
|
||||
},
|
||||
else => {
|
||||
%%io.stderr.printf("Process {} terminated unexpectedly\n", b.zig_exe);
|
||||
warn("Process {} terminated unexpectedly\n", b.zig_exe);
|
||||
return error.TestFailed;
|
||||
},
|
||||
};
|
||||
|
@ -597,7 +600,7 @@ pub const CompileErrorContext = struct {
|
|||
const stderr = stderr_buf.toSliceConst();
|
||||
|
||||
if (stdout.len != 0) {
|
||||
%%io.stderr.printf(
|
||||
warn(
|
||||
\\
|
||||
\\Expected empty stdout, instead found:
|
||||
\\================================================
|
||||
|
@ -610,7 +613,7 @@ pub const CompileErrorContext = struct {
|
|||
|
||||
for (self.case.expected_errors.toSliceConst()) |expected_error| {
|
||||
if (mem.indexOf(u8, stderr, expected_error) == null) {
|
||||
%%io.stderr.printf(
|
||||
warn(
|
||||
\\
|
||||
\\========= Expected this compile error: =========
|
||||
\\{}
|
||||
|
@ -621,15 +624,15 @@ pub const CompileErrorContext = struct {
|
|||
return error.TestFailed;
|
||||
}
|
||||
}
|
||||
%%io.stderr.printf("OK\n");
|
||||
warn("OK\n");
|
||||
}
|
||||
};
|
||||
|
||||
fn printInvocation(args: []const []const u8) {
|
||||
for (args) |arg| {
|
||||
%%io.stderr.printf("{} ", arg);
|
||||
warn("{} ", arg);
|
||||
}
|
||||
%%io.stderr.printf("\n");
|
||||
warn("\n");
|
||||
}
|
||||
|
||||
pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8,
|
||||
|
@ -822,7 +825,7 @@ pub const ParseCContext = struct {
|
|||
%%zig_args.append("parsec");
|
||||
%%zig_args.append(b.pathFromRoot(root_src));
|
||||
|
||||
%%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
|
||||
|
||||
if (b.verbose) {
|
||||
printInvocation(zig_args.toSliceConst());
|
||||
|
@ -841,8 +844,8 @@ pub const ParseCContext = struct {
|
|||
var stdout_buf = Buffer.initNull(b.allocator);
|
||||
var stderr_buf = Buffer.initNull(b.allocator);
|
||||
|
||||
%%(??child.stdout).readAll(&stdout_buf);
|
||||
%%(??child.stderr).readAll(&stderr_buf);
|
||||
%%(??child.stdout).in_stream.readAllBuffer(&stdout_buf, max_stdout_size);
|
||||
%%(??child.stderr).in_stream.readAllBuffer(&stderr_buf, max_stdout_size);
|
||||
|
||||
const term = child.wait() %% |err| {
|
||||
debug.panic("Unable to spawn {}: {}\n", zig_args.toSliceConst()[0], @errorName(err));
|
||||
|
@ -850,16 +853,16 @@ pub const ParseCContext = struct {
|
|||
switch (term) {
|
||||
Term.Exited => |code| {
|
||||
if (code != 0) {
|
||||
%%io.stderr.printf("Compilation failed with exit code {}\n", code);
|
||||
warn("Compilation failed with exit code {}\n", code);
|
||||
return error.TestFailed;
|
||||
}
|
||||
},
|
||||
Term.Signal => |code| {
|
||||
%%io.stderr.printf("Compilation failed with signal {}\n", code);
|
||||
warn("Compilation failed with signal {}\n", code);
|
||||
return error.TestFailed;
|
||||
},
|
||||
else => {
|
||||
%%io.stderr.printf("Compilation terminated unexpectedly\n");
|
||||
warn("Compilation terminated unexpectedly\n");
|
||||
return error.TestFailed;
|
||||
},
|
||||
};
|
||||
|
@ -868,7 +871,7 @@ pub const ParseCContext = struct {
|
|||
const stderr = stderr_buf.toSliceConst();
|
||||
|
||||
if (stderr.len != 0 and !self.case.allow_warnings) {
|
||||
%%io.stderr.printf(
|
||||
warn(
|
||||
\\====== parsec emitted warnings: ============
|
||||
\\{}
|
||||
\\============================================
|
||||
|
@ -879,7 +882,7 @@ pub const ParseCContext = struct {
|
|||
|
||||
for (self.case.expected_lines.toSliceConst()) |expected_line| {
|
||||
if (mem.indexOf(u8, stdout, expected_line) == null) {
|
||||
%%io.stderr.printf(
|
||||
warn(
|
||||
\\
|
||||
\\========= Expected this output: ================
|
||||
\\{}
|
||||
|
@ -890,15 +893,15 @@ pub const ParseCContext = struct {
|
|||
return error.TestFailed;
|
||||
}
|
||||
}
|
||||
%%io.stderr.printf("OK\n");
|
||||
warn("OK\n");
|
||||
}
|
||||
};
|
||||
|
||||
fn printInvocation(args: []const []const u8) {
|
||||
for (args) |arg| {
|
||||
%%io.stderr.printf("{} ", arg);
|
||||
warn("{} ", arg);
|
||||
}
|
||||
%%io.stderr.printf("\n");
|
||||
warn("\n");
|
||||
}
|
||||
|
||||
pub fn create(self: &ParseCContext, allow_warnings: bool, filename: []const u8, name: []const u8,
|
||||
|
|
Loading…
Reference in New Issue