Commit 9e234d4208
Changed files (21)
std
example/cat/main.zig
@@ -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;
};
}
example/guess_number/main.zig
@@ -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;
}
}
example/hello_world/hello.zig
@@ -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");
}
std/fmt/index.zig
@@ -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" {
std/os/child_process.zig
@@ -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.
std/os/index.zig
@@ -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);
+ }
+ }
+}
+
std/os/path.zig
@@ -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);
std/special/build_runner.zig
@@ -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;
};
}
std/special/test_runner.zig
@@ -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");
}
}
std/array_list.zig
@@ -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;
}
std/buffer.zig
@@ -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;
std/build.zig
@@ -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;
};
}
std/debug.zig
@@ -1,10 +1,11 @@
-const math = @import("math/index.zig");
-const mem = @import("mem.zig");
-const io = @import("io.zig");
-const os = @import("os/index.zig");
+const std = @import("index.zig");
+const math = std.math;
+const mem = std.mem;
+const io = std.io;
+const os = std.os;
const elf = @import("elf.zig");
const DW = @import("dwarf.zig");
-const ArrayList = @import("array_list.zig").ArrayList;
+const ArrayList = std.ArrayList;
const builtin = @import("builtin");
error MissingDebugInfo;
@@ -12,10 +13,42 @@ error InvalidDebugInfo;
error UnsupportedDebugInfo;
+/// Tries to write to stderr, unbuffered, and ignores any error returned.
+/// Does not append a newline.
+/// TODO atomic/multithread support
+var stderr_file: io.File = undefined;
+var stderr_stream: ?&io.OutStream = null;
+pub fn warn(comptime fmt: []const u8, args: ...) {
+ const stderr = getStderrStream() %% return;
+ stderr.print(fmt, args) %% return;
+}
+fn getStderrStream() -> %&io.OutStream {
+ if (stderr_stream) |st| {
+ return st;
+ } else {
+ stderr_file = %return io.getStdErr();
+ const st = &stderr_file.out_stream;
+ stderr_stream = st;
+ return st;
+ };
+}
+
+/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
+pub fn dumpStackTrace() {
+ const stderr = getStderrStream() %% return;
+ writeStackTrace(stderr, &global_allocator, stderr_file.isTty(), 1) %% return;
+}
+
+/// This function invokes undefined behavior when `ok` is `false`.
+/// In Debug and ReleaseSafe modes, calls to this function are always
+/// generated, and the `unreachable` statement triggers a panic.
+/// In ReleaseFast and ReleaseSmall modes, calls to this function can be
+/// optimized away.
pub fn assert(ok: bool) {
if (!ok) {
// In ReleaseFast test mode, we still want assert(false) to crash, so
// we insert an explicit call to @panic instead of unreachable.
+ // TODO we should use `assertOrPanic` in tests and remove this logic.
if (builtin.is_test) {
@panic("assertion failure")
} else {
@@ -24,6 +57,14 @@ pub fn assert(ok: bool) {
}
}
+/// Call this function when you want to panic if the condition is not true.
+/// If `ok` is `false`, this function will panic in every release mode.
+pub fn assertOrPanic(ok: bool) {
+ if (!ok) {
+ @panic("assertion failure");
+ }
+}
+
var panicking = false;
/// This is the default panic implementation.
pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
@@ -41,18 +82,13 @@ pub fn panic(comptime format: []const u8, args: ...) -> noreturn {
panicking = true;
}
- %%io.stderr.printf(format ++ "\n", args);
- %%writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty() %% false, 1);
- %%io.stderr.flush();
+ const stderr = getStderrStream() %% os.abort();
+ stderr.print(format ++ "\n", args) %% os.abort();
+ writeStackTrace(stderr, &global_allocator, stderr_file.isTty(), 1) %% os.abort();
os.abort();
}
-pub fn printStackTrace() -> %void {
- %return writeStackTrace(&io.stderr, &global_allocator, io.stderr.isTty() %% false, 1);
- %return io.stderr.flush();
-}
-
const GREEN = "\x1b[32;1m";
const WHITE = "\x1b[37;1m";
const DIM = "\x1b[2m";
@@ -69,7 +105,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
switch (builtin.object_format) {
builtin.ObjectFormat.elf => {
var stack_trace = ElfStackTrace {
- .self_exe_stream = undefined,
+ .self_exe_file = undefined,
.elf = undefined,
.debug_info = undefined,
.debug_abbrev = undefined,
@@ -79,10 +115,10 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
.compile_unit_list = ArrayList(CompileUnit).init(allocator),
};
const st = &stack_trace;
- st.self_exe_stream = %return io.openSelfExe();
- defer st.self_exe_stream.close();
+ st.self_exe_file = %return os.openSelfExe();
+ defer st.self_exe_file.close();
- %return st.elf.openStream(allocator, &st.self_exe_stream);
+ %return st.elf.openFile(allocator, &st.self_exe_file);
defer st.elf.close();
st.debug_info = (%return st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo;
@@ -109,7 +145,6 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
const compile_unit = findCompileUnit(st, return_address) ?? {
%return out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
return_address);
- %return out_stream.flush();
continue;
};
const compile_unit_name = %return compile_unit.die.getAttrString(st, DW.AT_name);
@@ -139,7 +174,6 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
},
else => return err,
};
- %return out_stream.flush();
}
},
builtin.ObjectFormat.coff => {
@@ -158,7 +192,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
}
fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_info: &const LineInfo) -> %void {
- var f = %return io.InStream.open(line_info.file_name, allocator);
+ var f = %return io.File.openRead(line_info.file_name, allocator);
defer f.close();
// TODO fstat and make sure that the file has the correct size
@@ -167,7 +201,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
var column: usize = 1;
var abs_index: usize = 0;
while (true) {
- const amt_read = %return f.read(buf[0..]);
+ const amt_read = %return f.in_stream.read(buf[0..]);
const slice = buf[0..amt_read];
for (slice) |byte| {
@@ -191,7 +225,7 @@ fn printLineFromFile(allocator: &mem.Allocator, out_stream: &io.OutStream, line_
}
const ElfStackTrace = struct {
- self_exe_stream: io.InStream,
+ self_exe_file: io.File,
elf: elf.Elf,
debug_info: &elf.SectionHeader,
debug_abbrev: &elf.SectionHeader,
@@ -205,7 +239,7 @@ const ElfStackTrace = struct {
}
pub fn readString(self: &ElfStackTrace) -> %[]u8 {
- return readStringRaw(self.allocator(), &self.self_exe_stream);
+ return readStringRaw(self.allocator(), &self.self_exe_file.in_stream);
}
};
@@ -424,7 +458,7 @@ fn readStringRaw(allocator: &mem.Allocator, in_stream: &io.InStream) -> %[]u8 {
fn getString(st: &ElfStackTrace, offset: u64) -> %[]u8 {
const pos = st.debug_str.offset + offset;
- %return st.self_exe_stream.seekTo(pos);
+ %return st.self_exe_file.seekTo(pos);
return st.readString();
}
@@ -533,7 +567,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
}
fn parseAbbrevTable(st: &ElfStackTrace) -> %AbbrevTable {
- const in_stream = &st.self_exe_stream;
+ const in_stream = &st.self_exe_file.in_stream;
var result = AbbrevTable.init(st.allocator());
while (true) {
const abbrev_code = %return readULeb128(in_stream);
@@ -568,7 +602,7 @@ fn getAbbrevTable(st: &ElfStackTrace, abbrev_offset: u64) -> %&const AbbrevTable
return &header.table;
}
}
- %return st.self_exe_stream.seekTo(st.debug_abbrev.offset + abbrev_offset);
+ %return st.self_exe_file.seekTo(st.debug_abbrev.offset + abbrev_offset);
%return st.abbrev_table_list.append(AbbrevTableHeader {
.offset = abbrev_offset,
.table = %return parseAbbrevTable(st),
@@ -585,8 +619,8 @@ fn getAbbrevTableEntry(abbrev_table: &const AbbrevTable, abbrev_code: u64) -> ?&
}
fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -> %Die {
- const in_stream = &st.self_exe_stream;
- const abbrev_code = %return readULeb128(in_stream);
+ const in_file = &st.self_exe_file;
+ const abbrev_code = %return readULeb128(&in_file.in_stream);
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo;
var result = Die {
@@ -598,7 +632,7 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -
for (table_entry.attrs.toSliceConst()) |attr, i| {
result.attrs.items[i] = Die.Attr {
.id = attr.attr_id,
- .value = %return parseFormValue(st.allocator(), &st.self_exe_stream, attr.form_id, is_64),
+ .value = %return parseFormValue(st.allocator(), &st.self_exe_file.in_stream, attr.form_id, is_64),
};
}
return result;
@@ -607,16 +641,16 @@ fn parseDie(st: &ElfStackTrace, abbrev_table: &const AbbrevTable, is_64: bool) -
fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, target_address: usize) -> %LineInfo {
const compile_unit_cwd = %return compile_unit.die.getAttrString(st, DW.AT_comp_dir);
- const in_stream = &st.self_exe_stream;
+ const in_file = &st.self_exe_file;
const debug_line_end = st.debug_line.offset + st.debug_line.size;
var this_offset = st.debug_line.offset;
var this_index: usize = 0;
while (this_offset < debug_line_end) : (this_index += 1) {
- %return in_stream.seekTo(this_offset);
+ %return in_file.seekTo(this_offset);
var is_64: bool = undefined;
- const unit_length = %return readInitialLength(in_stream, &is_64);
+ const unit_length = %return readInitialLength(&in_file.in_stream, &is_64);
if (unit_length == 0)
return error.MissingDebugInfo;
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
@@ -626,28 +660,28 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
continue;
}
- const version = %return in_stream.readInt(st.elf.is_big_endian, u16);
+ const version = %return in_file.in_stream.readInt(st.elf.is_big_endian, u16);
if (version != 2) return error.InvalidDebugInfo;
- const prologue_length = %return in_stream.readInt(st.elf.is_big_endian, u32);
- const prog_start_offset = (%return in_stream.getPos()) + prologue_length;
+ const prologue_length = %return in_file.in_stream.readInt(st.elf.is_big_endian, u32);
+ const prog_start_offset = (%return in_file.getPos()) + prologue_length;
- const minimum_instruction_length = %return in_stream.readByte();
+ const minimum_instruction_length = %return in_file.in_stream.readByte();
if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
- const default_is_stmt = (%return in_stream.readByte()) != 0;
- const line_base = %return in_stream.readByteSigned();
+ const default_is_stmt = (%return in_file.in_stream.readByte()) != 0;
+ const line_base = %return in_file.in_stream.readByteSigned();
- const line_range = %return in_stream.readByte();
+ const line_range = %return in_file.in_stream.readByte();
if (line_range == 0)
return error.InvalidDebugInfo;
- const opcode_base = %return in_stream.readByte();
+ const opcode_base = %return in_file.in_stream.readByte();
const standard_opcode_lengths = %return st.allocator().alloc(u8, opcode_base - 1);
{var i: usize = 0; while (i < opcode_base - 1) : (i += 1) {
- standard_opcode_lengths[i] = %return in_stream.readByte();
+ standard_opcode_lengths[i] = %return in_file.in_stream.readByte();
}}
var include_directories = ArrayList([]u8).init(st.allocator());
@@ -667,9 +701,9 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
const file_name = %return st.readString();
if (file_name.len == 0)
break;
- const dir_index = %return readULeb128(in_stream);
- const mtime = %return readULeb128(in_stream);
- const len_bytes = %return readULeb128(in_stream);
+ const dir_index = %return readULeb128(&in_file.in_stream);
+ const mtime = %return readULeb128(&in_file.in_stream);
+ const len_bytes = %return readULeb128(&in_file.in_stream);
%return file_entries.append(FileEntry {
.file_name = file_name,
.dir_index = dir_index,
@@ -678,42 +712,32 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
});
}
- %return in_stream.seekTo(prog_start_offset);
+ %return in_file.seekTo(prog_start_offset);
while (true) {
- //const pos = (%return in_stream.getPos()) - this_offset;
- //if (pos == 0x1a3) @breakpoint();
- //%%io.stderr.printf("\n{x8}\n", pos);
-
- const opcode = %return in_stream.readByte();
+ const opcode = %return in_file.in_stream.readByte();
var sub_op: u8 = undefined; // TODO move this to the correct scope and fix the compiler crash
if (opcode == DW.LNS_extended_op) {
- const op_size = %return readULeb128(in_stream);
+ const op_size = %return readULeb128(&in_file.in_stream);
if (op_size < 1)
return error.InvalidDebugInfo;
- sub_op = %return in_stream.readByte();
+ sub_op = %return in_file.in_stream.readByte();
switch (sub_op) {
DW.LNE_end_sequence => {
- //%%io.stdout.printf(" [0x{x8}] End Sequence\n", pos);
prog.end_sequence = true;
if (%return prog.checkLineMatch()) |info| return info;
return error.MissingDebugInfo;
},
DW.LNE_set_address => {
- const addr = %return in_stream.readInt(st.elf.is_big_endian, usize);
+ const addr = %return in_file.in_stream.readInt(st.elf.is_big_endian, usize);
prog.address = addr;
-
- //%%io.stdout.printf(" [0x{x8}] Extended opcode {}: set Address to 0x{x}\n",
- // pos, sub_op, addr);
},
DW.LNE_define_file => {
- //%%io.stdout.printf(" [0x{x8}] Define File\n", pos);
-
const file_name = %return st.readString();
- const dir_index = %return readULeb128(in_stream);
- const mtime = %return readULeb128(in_stream);
- const len_bytes = %return readULeb128(in_stream);
+ const dir_index = %return readULeb128(&in_file.in_stream);
+ const mtime = %return readULeb128(&in_file.in_stream);
+ const len_bytes = %return readULeb128(&in_file.in_stream);
%return file_entries.append(FileEntry {
.file_name = file_name,
.dir_index = dir_index,
@@ -723,7 +747,7 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
},
else => {
const fwd_amt = math.cast(isize, op_size - 1) %% return error.InvalidDebugInfo;
- %return in_stream.seekForward(fwd_amt);
+ %return in_file.seekForward(fwd_amt);
},
}
} else if (opcode >= opcode_base) {
@@ -733,48 +757,32 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
prog.line += inc_line;
prog.address += inc_addr;
- //%%io.stdout.printf(
- // " [0x{x8}] Special opcode {}: advance Address by {} to 0x{x} and Line by {} to {}\n",
- // pos, adjusted_opcode, inc_addr, prog.address, inc_line, prog.line);
if (%return prog.checkLineMatch()) |info| return info;
prog.basic_block = false;
} else {
switch (opcode) {
DW.LNS_copy => {
- //%%io.stdout.printf(" [0x{x8}] Copy\n", pos);
-
if (%return prog.checkLineMatch()) |info| return info;
prog.basic_block = false;
},
DW.LNS_advance_pc => {
- const arg = %return readULeb128(in_stream);
+ const arg = %return readULeb128(&in_file.in_stream);
prog.address += arg * minimum_instruction_length;
-
- //%%io.stdout.printf(" [0x{x8}] Advance PC by {} to 0x{x}\n", pos, arg, prog.address);
},
DW.LNS_advance_line => {
- const arg = %return readILeb128(in_stream);
+ const arg = %return readILeb128(&in_file.in_stream);
prog.line += arg;
-
- //%%io.stdout.printf(" [0x{x8}] Advance Line by {} to {}\n", pos, arg, prog.line);
},
DW.LNS_set_file => {
- const arg = %return readULeb128(in_stream);
+ const arg = %return readULeb128(&in_file.in_stream);
prog.file = arg;
-
- //%%io.stdout.printf(" [0x{x8}] Set File Name to entry {} in the File Name Table\n",
- // pos, arg);
},
DW.LNS_set_column => {
- const arg = %return readULeb128(in_stream);
+ const arg = %return readULeb128(&in_file.in_stream);
prog.column = arg;
-
- //%%io.stdout.printf(" [0x{x8}] Set column to {}\n", pos, arg);
},
DW.LNS_negate_stmt => {
prog.is_stmt = !prog.is_stmt;
-
- //%%io.stdout.printf(" [0x{x8}] Set is_stmt to {}\n", pos, if (prog.is_stmt) u8(1) else u8(0));
},
DW.LNS_set_basic_block => {
prog.basic_block = true;
@@ -782,23 +790,18 @@ fn getLineNumberInfo(st: &ElfStackTrace, compile_unit: &const CompileUnit, targe
DW.LNS_const_add_pc => {
const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
prog.address += inc_addr;
-
- //%%io.stdout.printf(" [0x{x8}] Advance PC by constant {} to 0x{x}\n",
- // pos, inc_addr, prog.address);
},
DW.LNS_fixed_advance_pc => {
- const arg = %return in_stream.readInt(st.elf.is_big_endian, u16);
+ const arg = %return in_file.in_stream.readInt(st.elf.is_big_endian, u16);
prog.address += arg;
},
DW.LNS_set_prologue_end => {
- //%%io.stdout.printf(" [0x{x8}] Set prologue_end to true\n", pos);
},
else => {
if (opcode - 1 >= standard_opcode_lengths.len)
return error.InvalidDebugInfo;
- //%%io.stdout.printf(" [0x{x8}] unknown op code {}\n", pos, opcode);
const len_bytes = standard_opcode_lengths[opcode - 1];
- %return in_stream.seekForward(len_bytes);
+ %return in_file.seekForward(len_bytes);
},
}
}
@@ -815,30 +818,30 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
var this_unit_offset = st.debug_info.offset;
var cu_index: usize = 0;
while (this_unit_offset < debug_info_end) {
- %return st.self_exe_stream.seekTo(this_unit_offset);
+ %return st.self_exe_file.seekTo(this_unit_offset);
var is_64: bool = undefined;
- const unit_length = %return readInitialLength(&st.self_exe_stream, &is_64);
+ const unit_length = %return readInitialLength(&st.self_exe_file.in_stream, &is_64);
if (unit_length == 0)
return;
const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
- const version = %return st.self_exe_stream.readInt(st.elf.is_big_endian, u16);
+ const version = %return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u16);
if (version < 2 or version > 5) return error.InvalidDebugInfo;
const debug_abbrev_offset = if (is_64) {
- %return st.self_exe_stream.readInt(st.elf.is_big_endian, u64)
+ %return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u64)
} else {
- %return st.self_exe_stream.readInt(st.elf.is_big_endian, u32)
+ %return st.self_exe_file.in_stream.readInt(st.elf.is_big_endian, u32)
};
- const address_size = %return st.self_exe_stream.readByte();
+ const address_size = %return st.self_exe_file.in_stream.readByte();
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
- const compile_unit_pos = %return st.self_exe_stream.getPos();
+ const compile_unit_pos = %return st.self_exe_file.getPos();
const abbrev_table = %return getAbbrevTable(st, debug_abbrev_offset);
- %return st.self_exe_stream.seekTo(compile_unit_pos);
+ %return st.self_exe_file.seekTo(compile_unit_pos);
const compile_unit_die = %return st.allocator().create(Die);
*compile_unit_die = %return parseDie(st, abbrev_table, is_64);
std/elf.zig
@@ -1,7 +1,9 @@
-const io = @import("io.zig");
-const math = @import("math/index.zig");
-const mem = @import("mem.zig");
-const debug = @import("debug.zig");
+const std = @import("index.zig");
+const io = std.io;
+const math = std.math;
+const mem = std.mem;
+const debug = std.debug;
+const InStream = std.stream.InStream;
error InvalidFormat;
@@ -62,7 +64,7 @@ pub const SectionHeader = struct {
};
pub const Elf = struct {
- in_stream: &io.InStream,
+ in_file: &io.File,
auto_close_stream: bool,
is_64: bool,
is_big_endian: bool,
@@ -75,44 +77,44 @@ pub const Elf = struct {
string_section: &SectionHeader,
section_headers: []SectionHeader,
allocator: &mem.Allocator,
- prealloc_stream: io.InStream,
+ prealloc_file: io.File,
/// Call close when done.
- pub fn openFile(elf: &Elf, allocator: &mem.Allocator, path: []const u8) -> %void {
- %return elf.prealloc_stream.open(path);
- %return elf.openStream(allocator, &elf.prealloc_stream);
+ pub fn openPath(elf: &Elf, allocator: &mem.Allocator, path: []const u8) -> %void {
+ %return elf.prealloc_file.open(path);
+ %return elf.openFile(allocator, &elf.prealloc_file);
elf.auto_close_stream = true;
}
/// Call close when done.
- pub fn openStream(elf: &Elf, allocator: &mem.Allocator, stream: &io.InStream) -> %void {
+ pub fn openFile(elf: &Elf, allocator: &mem.Allocator, file: &io.File) -> %void {
elf.allocator = allocator;
- elf.in_stream = stream;
+ elf.in_file = file;
elf.auto_close_stream = false;
var magic: [4]u8 = undefined;
- %return elf.in_stream.readNoEof(magic[0..]);
+ %return elf.in_file.in_stream.readNoEof(magic[0..]);
if (!mem.eql(u8, magic, "\x7fELF")) return error.InvalidFormat;
- elf.is_64 = switch (%return elf.in_stream.readByte()) {
+ elf.is_64 = switch (%return elf.in_file.in_stream.readByte()) {
1 => false,
2 => true,
else => return error.InvalidFormat,
};
- elf.is_big_endian = switch (%return elf.in_stream.readByte()) {
+ elf.is_big_endian = switch (%return elf.in_file.in_stream.readByte()) {
1 => false,
2 => true,
else => return error.InvalidFormat,
};
- const version_byte = %return elf.in_stream.readByte();
+ const version_byte = %return elf.in_file.in_stream.readByte();
if (version_byte != 1) return error.InvalidFormat;
// skip over padding
- %return elf.in_stream.seekForward(9);
+ %return elf.in_file.seekForward(9);
- elf.file_type = switch (%return elf.in_stream.readInt(elf.is_big_endian, u16)) {
+ elf.file_type = switch (%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16)) {
1 => FileType.Relocatable,
2 => FileType.Executable,
3 => FileType.Shared,
@@ -120,7 +122,7 @@ pub const Elf = struct {
else => return error.InvalidFormat,
};
- elf.arch = switch (%return elf.in_stream.readInt(elf.is_big_endian, u16)) {
+ elf.arch = switch (%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16)) {
0x02 => Arch.Sparc,
0x03 => Arch.x86,
0x08 => Arch.Mips,
@@ -133,34 +135,34 @@ pub const Elf = struct {
else => return error.InvalidFormat,
};
- const elf_version = %return elf.in_stream.readInt(elf.is_big_endian, u32);
+ const elf_version = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
if (elf_version != 1) return error.InvalidFormat;
if (elf.is_64) {
- elf.entry_addr = %return elf.in_stream.readInt(elf.is_big_endian, u64);
- elf.program_header_offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
- elf.section_header_offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
+ elf.entry_addr = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
+ elf.program_header_offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
+ elf.section_header_offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
} else {
- elf.entry_addr = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
- elf.program_header_offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
- elf.section_header_offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
+ elf.entry_addr = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
+ elf.program_header_offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
+ elf.section_header_offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
}
// skip over flags
- %return elf.in_stream.seekForward(4);
+ %return elf.in_file.seekForward(4);
- const header_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
+ const header_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
if ((elf.is_64 and header_size != 64) or
(!elf.is_64 and header_size != 52))
{
return error.InvalidFormat;
}
- const ph_entry_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
- const ph_entry_count = %return elf.in_stream.readInt(elf.is_big_endian, u16);
- const sh_entry_size = %return elf.in_stream.readInt(elf.is_big_endian, u16);
- const sh_entry_count = %return elf.in_stream.readInt(elf.is_big_endian, u16);
- elf.string_section_index = u64(%return elf.in_stream.readInt(elf.is_big_endian, u16));
+ const ph_entry_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
+ const ph_entry_count = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
+ const sh_entry_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
+ const sh_entry_count = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u16);
+ elf.string_section_index = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u16));
if (elf.string_section_index >= sh_entry_count) return error.InvalidFormat;
@@ -169,12 +171,12 @@ pub const Elf = struct {
const ph_byte_count = u64(ph_entry_size) * u64(ph_entry_count);
const end_ph = %return math.add(u64, elf.program_header_offset, ph_byte_count);
- const stream_end = %return elf.in_stream.getEndPos();
+ const stream_end = %return elf.in_file.getEndPos();
if (stream_end < end_sh or stream_end < end_ph) {
return error.InvalidFormat;
}
- %return elf.in_stream.seekTo(elf.section_header_offset);
+ %return elf.in_file.seekTo(elf.section_header_offset);
elf.section_headers = %return elf.allocator.alloc(SectionHeader, sh_entry_count);
%defer elf.allocator.free(elf.section_headers);
@@ -183,32 +185,32 @@ pub const Elf = struct {
if (sh_entry_size != 64) return error.InvalidFormat;
for (elf.section_headers) |*section| {
- section.name = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.sh_type = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.flags = %return elf.in_stream.readInt(elf.is_big_endian, u64);
- section.addr = %return elf.in_stream.readInt(elf.is_big_endian, u64);
- section.offset = %return elf.in_stream.readInt(elf.is_big_endian, u64);
- section.size = %return elf.in_stream.readInt(elf.is_big_endian, u64);
- section.link = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.info = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.addr_align = %return elf.in_stream.readInt(elf.is_big_endian, u64);
- section.ent_size = %return elf.in_stream.readInt(elf.is_big_endian, u64);
+ section.name = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.sh_type = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.flags = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
+ section.addr = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
+ section.offset = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
+ section.size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
+ section.link = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.info = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.addr_align = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
+ section.ent_size = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u64);
}
} else {
if (sh_entry_size != 40) return error.InvalidFormat;
for (elf.section_headers) |*section| {
// TODO (multiple occurences) allow implicit cast from %u32 -> %u64 ?
- section.name = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.sh_type = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.flags = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
- section.addr = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
- section.offset = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
- section.size = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
- section.link = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.info = %return elf.in_stream.readInt(elf.is_big_endian, u32);
- section.addr_align = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
- section.ent_size = u64(%return elf.in_stream.readInt(elf.is_big_endian, u32));
+ section.name = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.sh_type = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.flags = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
+ section.addr = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
+ section.offset = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
+ section.size = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
+ section.link = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.info = %return elf.in_file.in_stream.readInt(elf.is_big_endian, u32);
+ section.addr_align = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
+ section.ent_size = u64(%return elf.in_file.in_stream.readInt(elf.is_big_endian, u32));
}
}
@@ -230,7 +232,7 @@ pub const Elf = struct {
elf.allocator.free(elf.section_headers);
if (elf.auto_close_stream)
- elf.in_stream.close();
+ elf.in_file.close();
}
pub fn findSection(elf: &Elf, name: []const u8) -> %?&SectionHeader {
@@ -238,15 +240,15 @@ pub const Elf = struct {
if (section.sh_type == SHT_NULL) continue;
const name_offset = elf.string_section.offset + section.name;
- %return elf.in_stream.seekTo(name_offset);
+ %return elf.in_file.seekTo(name_offset);
for (name) |expected_c| {
- const target_c = %return elf.in_stream.readByte();
+ const target_c = %return elf.in_file.in_stream.readByte();
if (target_c == 0 or expected_c != target_c) goto next_section;
}
{
- const null_byte = %return elf.in_stream.readByte();
+ const null_byte = %return elf.in_file.in_stream.readByte();
if (null_byte == 0) return section;
}
@@ -257,6 +259,6 @@ pub const Elf = struct {
}
pub fn seekToSection(elf: &Elf, section: &SectionHeader) -> %void {
- %return elf.in_stream.seekTo(section.offset);
+ %return elf.in_file.seekTo(section.offset);
}
};
std/heap.zig
@@ -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);
+}
+
std/index.zig
@@ -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");
std/io.zig
@@ -1,3 +1,4 @@
+const std = @import("index.zig");
const builtin = @import("builtin");
const Os = builtin.Os;
const system = switch(builtin.os) {
@@ -6,41 +7,19 @@ const system = switch(builtin.os) {
Os.windows => @import("os/windows/index.zig"),
else => @compileError("Unsupported OS"),
};
-const c = @import("c/index.zig");
+const c = std.c;
-const math = @import("math/index.zig");
-const debug = @import("debug.zig");
+const math = std.math;
+const debug = std.debug;
const assert = debug.assert;
-const os = @import("os/index.zig");
-const mem = @import("mem.zig");
-const Buffer = @import("buffer.zig").Buffer;
-const fmt = @import("fmt/index.zig");
+const os = std.os;
+const mem = std.mem;
+const Buffer = std.Buffer;
+const fmt = std.fmt;
const is_posix = builtin.os != builtin.Os.windows;
const is_windows = builtin.os == builtin.Os.windows;
-pub var stdin = InStream {
- .fd = if (is_posix) system.STDIN_FILENO else {},
- .handle_id = if (is_windows) system.STD_INPUT_HANDLE else {},
- .handle = if (is_windows) null else {},
-};
-
-pub var stdout = OutStream {
- .fd = if (is_posix) system.STDOUT_FILENO else {},
- .handle_id = if (is_windows) system.STD_OUTPUT_HANDLE else {},
- .handle = if (is_windows) null else {},
- .buffer = undefined,
- .index = 0,
-};
-
-pub var stderr = OutStream {
- .fd = if (is_posix) system.STDERR_FILENO else {},
- .handle_id = if (is_windows) system.STD_ERROR_HANDLE else {},
- .handle = if (is_windows) null else {},
- .buffer = undefined,
- .index = 0,
-};
-
/// The function received invalid input at runtime. An Invalid error means a
/// bug in the program that called the function.
error Invalid;
@@ -63,306 +42,125 @@ 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,
-
- /// Calls ::openMode with 0o666 for the mode.
- pub fn open(path: []const u8, allocator: ?&mem.Allocator) -> %OutStream {
- return openMode(path, 0o666, allocator);
- }
-
- /// `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 openMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) -> %OutStream {
- 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,
- };
- } 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,
- };
-
- } 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 = {},
- };
- _ = fmt.format(&context, printOutput, format, args);
- return context.result;
- }
- const PrintContext = struct {
- self: &OutStream,
- result: %void,
+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
};
- 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;
- }
- }
+ return File.openHandle(handle);
+}
- 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;
- }
- }
+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);
+}
- 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;
- }
- }
+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);
+}
- 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");
- }
- }
+pub const File = struct {
+ /// The OS-specific file descriptor or file handle.
+ handle: os.FileHandle,
-};
+ /// A file has the `InStream` trait
+ in_stream: InStream,
-// 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,
+ /// 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 open(path: []const u8, allocator: ?&mem.Allocator) -> %InStream {
+ 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 InStream {
- .fd = fd,
- .handle_id = {},
- .handle = {},
- };
+ 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 InStream {
- .fd = {},
- .handle_id = undefined,
- .handle = handle,
- };
+ return openHandle(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;
- }
+ /// Calls `openWriteMode` with 0o666 for the mode.
+ pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) -> %File {
+ return openWriteMode(path, 0o666, allocator);
+
}
- /// 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 {
+ /// `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 openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) -> %File {
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;
+ 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 openHandle(fd);
} 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;
+ 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 openHandle(handle);
} else {
unreachable;
}
- }
- 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 openHandle(handle: os.FileHandle) -> File {
+ return File {
+ .handle = handle,
+ .out_stream = OutStream {
+ .writeFn = writeFn,
+ },
+ .in_stream = InStream {
+ .readFn = readFn,
+ },
+ };
}
- 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);
+ /// Upon success, the stream is in an uninitialized state. To continue using it,
+ /// you must use the open() function.
+ pub fn close(self: &File) {
+ os.close(self.handle);
+ self.handle = undefined;
}
- 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);
+ /// Calls `os.isTty` on `self.handle`.
+ pub fn isTty(self: &File) -> bool {
+ return os.isTty(self.handle);
}
- 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);
+ fn readFn(in_stream: &InStream, buffer: []u8) -> %usize {
+ const self = @fieldParentPtr(File, "in_stream", in_stream);
+ if (is_posix) {
+ 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) {
+ 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");
+ }
+ }
+
+};
+
+/// `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.
+pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator) -> %void {
+ 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 = buf.toSlice()[actual_buf_len..];
- const bytes_read = %return is.read(dest_slice);
+ 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) {
- return buf.resize(actual_buf_len);
+ buffer.shrink(actual_buf_len);
+ return;
}
- %return buf.resize(actual_buf_len + os.page_size);
+ 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);
}
}
- pub fn readLine(is: &InStream, buf: &Buffer) -> %void {
+ /// 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 is.readByte();
- %return buf.appendByte(byte);
+ var byte: u8 = %return self.readByte();
- if (buf.endsWith(os.line_sep)) {
- break;
+ if (byte == delimiter) {
+ return;
}
- }
- }
- pub fn isTty(self: &InStream) -> %bool {
- if (is_posix) {
- if (builtin.link_libc) {
- return c.isatty(self.fd) != 0;
- } else {
- return system.isatty(self.fd);
+ if (buf.len() == max_size) {
+ return error.StreamTooLong;
}
- } else if (is_windows) {
- return os.windowsIsTty(%return self.getHandle());
- } else {
- @compileError("Unsupported OS");
+
+ %return buf.appendByte(byte);
}
}
- 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;
- }
+ /// 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 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"),
+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);
}
-}
-/// `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.
-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();
-}
+ 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);
+ }
+};
std/mem.zig
@@ -1,16 +1,9 @@
const debug = @import("debug.zig");
const assert = debug.assert;
const math = @import("math/index.zig");
-const os = @import("os/index.zig");
-const io = @import("io.zig");
-const builtin = @import("builtin");
-const Os = builtin.Os;
-const c = @import("c/index.zig");
pub const Cmp = math.Cmp;
-error OutOfMemory;
-
pub const Allocator = struct {
/// Allocate byte_count bytes and return them in a slice, with the
/// slicer's pointer aligned at least to alignment bytes.
@@ -85,151 +78,6 @@ pub const Allocator = struct {
}
};
-pub var c_allocator = Allocator {
- .allocFn = cAlloc,
- .reallocFn = cRealloc,
- .freeFn = cFree,
-};
-
-fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
- if (c.malloc(usize(n))) |mem| {
- @ptrCast(&u8, mem)[0..n]
- } else {
- error.OutOfMemory
- }
-}
-
-fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
- if (new_size <= old_mem.len) {
- old_mem[0..new_size]
- } else {
- const old_ptr = @ptrCast(&c_void, old_mem.ptr);
- if (c.realloc(old_ptr, usize(new_size))) |mem| {
- @ptrCast(&u8, mem)[0..new_size]
- } else {
- error.OutOfMemory
- }
- }
-}
-
-fn cFree(self: &Allocator, old_mem: []u8) {
- const old_ptr = @ptrCast(&c_void, old_mem.ptr);
- c.free(old_ptr);
-}
-
-pub const IncrementingAllocator = struct {
- allocator: Allocator,
- bytes: []u8,
- end_index: usize,
- heap_handle: if (builtin.os == Os.windows) os.windows.HANDLE else void,
-
- fn init(capacity: usize) -> %IncrementingAllocator {
- switch (builtin.os) {
- Os.linux, Os.darwin, Os.macosx, Os.ios => {
- const p = os.posix;
- const addr = p.mmap(null, capacity, p.PROT_READ|p.PROT_WRITE,
- p.MAP_PRIVATE|p.MAP_ANONYMOUS|p.MAP_NORESERVE, -1, 0);
- if (addr == p.MAP_FAILED) {
- return error.OutOfMemory;
- }
- return IncrementingAllocator {
- .allocator = Allocator {
- .allocFn = alloc,
- .reallocFn = realloc,
- .freeFn = free,
- },
- .bytes = @intToPtr(&u8, addr)[0..capacity],
- .end_index = 0,
- .heap_handle = {},
- };
- },
- Os.windows => {
- const heap_handle = os.windows.GetProcessHeap() ?? return error.OutOfMemory;
- const ptr = os.windows.HeapAlloc(heap_handle, 0, capacity) ?? return error.OutOfMemory;
- return IncrementingAllocator {
- .allocator = Allocator {
- .allocFn = alloc,
- .reallocFn = realloc,
- .freeFn = free,
- },
- .bytes = @ptrCast(&u8, ptr)[0..capacity],
- .end_index = 0,
- .heap_handle = heap_handle,
- };
- },
- else => @compileError("Unsupported OS"),
- }
- }
-
- fn deinit(self: &IncrementingAllocator) {
- switch (builtin.os) {
- Os.linux, Os.darwin, Os.macosx, Os.ios => {
- _ = os.posix.munmap(self.bytes.ptr, self.bytes.len);
- },
- Os.windows => {
- _ = os.windows.HeapFree(self.heap_handle, 0, @ptrCast(os.windows.LPVOID, self.bytes.ptr));
- },
- else => @compileError("Unsupported OS"),
- }
- }
-
- fn reset(self: &IncrementingAllocator) {
- self.end_index = 0;
- }
-
- fn bytesLeft(self: &const IncrementingAllocator) -> usize {
- return self.bytes.len - self.end_index;
- }
-
- fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
- const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
- const addr = @ptrToInt(&self.bytes[self.end_index]);
- const rem = @rem(addr, alignment);
- const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
- const adjusted_index = self.end_index + march_forward_bytes;
- const new_end_index = adjusted_index + n;
- if (new_end_index > self.bytes.len) {
- return error.OutOfMemory;
- }
- const result = self.bytes[adjusted_index .. new_end_index];
- self.end_index = new_end_index;
- return result;
- }
-
- fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
- if (new_size <= old_mem.len) {
- return old_mem[0..new_size];
- } else {
- const result = %return alloc(allocator, new_size, alignment);
- copy(u8, result, old_mem);
- return result;
- }
- }
-
- fn free(allocator: &Allocator, bytes: []u8) {
- // Do nothing. That's the point of an incrementing allocator.
- }
-};
-
-test "mem.IncrementingAllocator" {
- const total_bytes = 100 * 1024 * 1024;
- var inc_allocator = %%IncrementingAllocator.init(total_bytes);
- defer inc_allocator.deinit();
-
- const allocator = &inc_allocator.allocator;
- const slice = %%allocator.alloc(&i32, 100);
-
- for (slice) |*item, i| {
- *item = %%allocator.create(i32);
- **item = i32(i);
- }
-
- assert(inc_allocator.bytesLeft() == total_bytes - @sizeOf(i32) * 100 - @sizeOf(usize) * 100);
-
- inc_allocator.reset();
-
- assert(inc_allocator.bytesLeft() == total_bytes);
-}
/// Copy all of source into dest at position 0.
/// dest.len must be >= source.len.
test/compare_output.zig
@@ -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");
test/tests.zig
@@ -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,
CMakeLists.txt
@@ -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")