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
Andrew Kelley 2017-10-31 04:47:55 -04:00
parent 7a96aca39e
commit 9e234d4208
21 changed files with 978 additions and 1040 deletions

View File

@ -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")

View File

@ -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;
};
}

View File

@ -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;
}
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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);
}
};

View File

@ -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" {

158
std/heap.zig Normal file
View File

@ -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);
}

View File

@ -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");

View File

@ -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);
}
};

View File

@ -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.

View File

@ -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.

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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;
};
}

View File

@ -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");
}
}

View File

@ -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");

View File

@ -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,