Skip to content

Commit

Permalink
std lib: modify allocator idiom
Browse files Browse the repository at this point in the history
Before we accepted a nullable allocator for some stuff like
opening files. Now we require an allocator.

Use the mem.FixedBufferAllocator pattern if a bound on the amount
to allocate is known.

This also establishes the pattern that usually an allocator is the
first argument to a function (possibly after "self").

fix docs for std.cstr.addNullByte

self hosted compiler:
 * only build docs when explicitly asked to
 * clean up main
 * stub out zig fmt
  • Loading branch information
andrewrk committed Feb 9, 2018
1 parent e7bf8f3 commit a2bd9f8
Show file tree
Hide file tree
Showing 15 changed files with 103 additions and 136 deletions.
1 change: 0 additions & 1 deletion build.zig
Expand Up @@ -78,7 +78,6 @@ pub fn build(b: &Builder) !void {
exe.linkSystemLibrary("c");

b.default_step.dependOn(&exe.step);
b.default_step.dependOn(docs_step);

const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false;
if (!skip_self_hosted) {
Expand Down
6 changes: 3 additions & 3 deletions doc/docgen.zig
Expand Up @@ -31,10 +31,10 @@ pub fn main() !void {
const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg"));
defer allocator.free(out_file_name);

var in_file = try io.File.openRead(in_file_name, allocator);
var in_file = try io.File.openRead(allocator, in_file_name);
defer in_file.close();

var out_file = try io.File.openWrite(out_file_name, allocator);
var out_file = try io.File.openWrite(allocator, out_file_name);
defer out_file.close();

var file_in_stream = io.FileInStream.init(&in_file);
Expand Down Expand Up @@ -723,7 +723,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
try out.print("<pre><code class=\"zig\">{}</code></pre>", escaped_source);
const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
try io.writeFile(tmp_source_file_name, trimmed_raw_source, null);
try io.writeFile(allocator, tmp_source_file_name, trimmed_raw_source);

switch (code.id) {
Code.Id.Exe => |expected_outcome| {
Expand Down
2 changes: 1 addition & 1 deletion example/cat/main.zig
Expand Up @@ -20,7 +20,7 @@ pub fn main() !void {
} else if (arg[0] == '-') {
return usage(exe);
} else {
var file = io.File.openRead(arg, null) catch |err| {
var file = io.File.openRead(allocator, arg) catch |err| {
warn("Unable to open file: {}\n", @errorName(err));
return err;
};
Expand Down
65 changes: 35 additions & 30 deletions src-self-hosted/main.zig
Expand Up @@ -16,15 +16,6 @@ const c = @import("c.zig");

const default_zig_cache_name = "zig-cache";

pub fn main() !void {
main2() catch |err| {
if (err != error.InvalidCommandLineArguments) {
warn("{}\n", @errorName(err));
}
return err;
};
}

const Cmd = enum {
None,
Build,
Expand All @@ -35,21 +26,25 @@ const Cmd = enum {
Targets,
};

fn badArgs(comptime format: []const u8, args: ...) error {
var stderr = try io.getStdErr();
fn badArgs(comptime format: []const u8, args: ...) noreturn {
var stderr = io.getStdErr() catch std.os.exit(1);
var stderr_stream_adapter = io.FileOutStream.init(&stderr);
const stderr_stream = &stderr_stream_adapter.stream;
try stderr_stream.print(format ++ "\n\n", args);
try printUsage(&stderr_stream_adapter.stream);
return error.InvalidCommandLineArguments;
stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
std.os.exit(1);
}

pub fn main2() !void {
pub fn main() !void {
const allocator = std.heap.c_allocator;

const args = try os.argsAlloc(allocator);
defer os.argsFree(allocator, args);

if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
return fmtMain(allocator, args[2..]);
}

var cmd = Cmd.None;
var build_kind: Module.Kind = undefined;
var build_mode: builtin.Mode = builtin.Mode.Debug;
Expand Down Expand Up @@ -169,7 +164,7 @@ pub fn main2() !void {
} else if (mem.eql(u8, arg, "--pkg-end")) {
@panic("TODO --pkg-end");
} else if (arg_i + 1 >= args.len) {
return badArgs("expected another argument after {}", arg);
badArgs("expected another argument after {}", arg);
} else {
arg_i += 1;
if (mem.eql(u8, arg, "--output")) {
Expand All @@ -184,7 +179,7 @@ pub fn main2() !void {
} else if (mem.eql(u8, args[arg_i], "off")) {
color = ErrColor.Off;
} else {
return badArgs("--color options are 'auto', 'on', or 'off'");
badArgs("--color options are 'auto', 'on', or 'off'");
}
} else if (mem.eql(u8, arg, "--emit")) {
if (mem.eql(u8, args[arg_i], "asm")) {
Expand All @@ -194,7 +189,7 @@ pub fn main2() !void {
} else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
emit_file_type = Emit.LlvmIr;
} else {
return badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
}
} else if (mem.eql(u8, arg, "--name")) {
out_name_arg = args[arg_i];
Expand Down Expand Up @@ -262,7 +257,7 @@ pub fn main2() !void {
} else if (mem.eql(u8, arg, "--test-cmd")) {
@panic("TODO --test-cmd");
} else {
return badArgs("invalid argument: {}", arg);
badArgs("invalid argument: {}", arg);
}
}
} else if (cmd == Cmd.None) {
Expand All @@ -285,18 +280,18 @@ pub fn main2() !void {
cmd = Cmd.Test;
build_kind = Module.Kind.Exe;
} else {
return badArgs("unrecognized command: {}", arg);
badArgs("unrecognized command: {}", arg);
}
} else switch (cmd) {
Cmd.Build, Cmd.TranslateC, Cmd.Test => {
if (in_file_arg == null) {
in_file_arg = arg;
} else {
return badArgs("unexpected extra parameter: {}", arg);
badArgs("unexpected extra parameter: {}", arg);
}
},
Cmd.Version, Cmd.Zen, Cmd.Targets => {
return badArgs("unexpected extra parameter: {}", arg);
badArgs("unexpected extra parameter: {}", arg);
},
Cmd.None => unreachable,
}
Expand Down Expand Up @@ -333,15 +328,15 @@ pub fn main2() !void {
// }

switch (cmd) {
Cmd.None => return badArgs("expected command"),
Cmd.None => badArgs("expected command"),
Cmd.Zen => return printZen(),
Cmd.Build, Cmd.Test, Cmd.TranslateC => {
if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
return badArgs("expected source file argument or at least one --object or --assembly argument");
badArgs("expected source file argument or at least one --object or --assembly argument");
} else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
return badArgs("expected source file argument");
badArgs("expected source file argument");
} else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
return badArgs("When building an object file, --object arguments are invalid");
badArgs("When building an object file, --object arguments are invalid");
}

const root_name = switch (cmd) {
Expand All @@ -351,9 +346,9 @@ pub fn main2() !void {
} else if (in_file_arg) |in_file_path| {
const basename = os.path.basename(in_file_path);
var it = mem.split(basename, ".");
break :x it.next() ?? return badArgs("file name cannot be empty");
break :x it.next() ?? badArgs("file name cannot be empty");
} else {
return badArgs("--name [name] not provided and unable to infer");
badArgs("--name [name] not provided and unable to infer");
}
},
Cmd.Test => "test",
Expand Down Expand Up @@ -428,7 +423,7 @@ pub fn main2() !void {
module.linker_rdynamic = rdynamic;

if (mmacosx_version_min != null and mios_version_min != null) {
return badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
}

if (mmacosx_version_min) |ver| {
Expand Down Expand Up @@ -477,6 +472,7 @@ fn printUsage(stream: var) !void {
\\ build-exe [source] create executable from source or object files
\\ build-lib [source] create library from source or object files
\\ build-obj [source] create object from source or assembly
\\ fmt [file] parse file and render in canonical zig format
\\ translate-c [source] convert c code to zig code
\\ targets list available compilation targets
\\ test [source] create and run a test build
Expand Down Expand Up @@ -564,6 +560,15 @@ fn printZen() !void {
);
}

fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
for (file_paths) |file_path| {
var file = try io.File.openRead(allocator, file_path);
defer file.close();

warn("opened {} (todo tokenize and parse and render)\n", file_path);
}
}

/// Caller must free result
fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {
if (zig_install_prefix_arg) |zig_install_prefix| {
Expand All @@ -588,7 +593,7 @@ fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8
const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
defer allocator.free(test_index_file);

var file = try io.File.openRead(test_index_file, allocator);
var file = try io.File.openRead(allocator, test_index_file);
file.close();

return test_zig_dir;
Expand Down
2 changes: 1 addition & 1 deletion src-self-hosted/module.zig
Expand Up @@ -213,7 +213,7 @@ pub const Module = struct {
};
errdefer self.allocator.free(root_src_real_path);

const source_code = io.readFileAllocExtra(root_src_real_path, self.allocator, 3) catch |err| {
const source_code = io.readFileAllocExtra(self.allocator, root_src_real_path, 3) catch |err| {
try printError("unable to open '{}': {}", root_src_real_path, err);
return err;
};
Expand Down
2 changes: 1 addition & 1 deletion std/build.zig
Expand Up @@ -1890,7 +1890,7 @@ pub const WriteFileStep = struct {
warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
return err;
};
io.writeFile(full_path, self.data, self.builder.allocator) catch |err| {
io.writeFile(self.builder.allocator, full_path, self.data) catch |err| {
warn("unable to write {}: {}\n", full_path, @errorName(err));
return err;
};
Expand Down
3 changes: 1 addition & 2 deletions std/cstr.zig
Expand Up @@ -39,8 +39,7 @@ fn testCStrFnsImpl() void {
assert(len(c"123456789") == 9);
}

/// Returns a mutable slice with exactly the same size which is guaranteed to
/// have a null byte after it.
/// Returns a mutable slice with 1 more byte of length which is a null byte.
/// Caller owns the returned memory.
pub fn addNullByte(allocator: &mem.Allocator, slice: []const u8) ![]u8 {
const result = try allocator.alloc(u8, slice.len + 1);
Expand Down
2 changes: 1 addition & 1 deletion std/debug/index.zig
Expand Up @@ -265,7 +265,7 @@ pub fn openSelfDebugInfo(allocator: &mem.Allocator) !&ElfStackTrace {
}

fn printLineFromFile(allocator: &mem.Allocator, out_stream: var, line_info: &const LineInfo) !void {
var f = try io.File.openRead(line_info.file_name, allocator);
var f = try io.File.openRead(allocator, line_info.file_name);
defer f.close();
// TODO fstat and make sure that the file has the correct size

Expand Down
47 changes: 19 additions & 28 deletions std/io.zig
Expand Up @@ -110,45 +110,39 @@ pub const File = struct {

const OpenError = os.WindowsOpenError || os.PosixOpenError;

/// `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.
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
/// Call close to clean up.
pub fn openRead(path: []const u8, allocator: ?&mem.Allocator) OpenError!File {
pub fn openRead(allocator: &mem.Allocator, path: []const u8) OpenError!File {
if (is_posix) {
const flags = system.O_LARGEFILE|system.O_RDONLY;
const fd = try os.posixOpen(path, flags, 0, allocator);
const fd = try os.posixOpen(allocator, path, flags, 0);
return openHandle(fd);
} else if (is_windows) {
const handle = try os.windowsOpen(path, system.GENERIC_READ, system.FILE_SHARE_READ,
system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL, allocator);
const handle = try os.windowsOpen(allocator, path, system.GENERIC_READ, system.FILE_SHARE_READ,
system.OPEN_EXISTING, system.FILE_ATTRIBUTE_NORMAL);
return openHandle(handle);
} else {
unreachable;
}
}

/// Calls `openWriteMode` with 0o666 for the mode.
pub fn openWrite(path: []const u8, allocator: ?&mem.Allocator) !File {
return openWriteMode(path, 0o666, allocator);
pub fn openWrite(allocator: &mem.Allocator, path: []const u8) !File {
return openWriteMode(allocator, path, 0o666);

}

/// `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.
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
/// Call close to clean up.
pub fn openWriteMode(path: []const u8, mode: usize, allocator: ?&mem.Allocator) !File {
pub fn openWriteMode(allocator: &mem.Allocator, path: []const u8, mode: usize) !File {
if (is_posix) {
const flags = system.O_LARGEFILE|system.O_WRONLY|system.O_CREAT|system.O_CLOEXEC|system.O_TRUNC;
const fd = try os.posixOpen(path, flags, mode, allocator);
const fd = try os.posixOpen(allocator, path, flags, mode);
return openHandle(fd);
} else if (is_windows) {
const handle = try os.windowsOpen(path, system.GENERIC_WRITE,
const handle = try os.windowsOpen(allocator, path, system.GENERIC_WRITE,
system.FILE_SHARE_WRITE|system.FILE_SHARE_READ|system.FILE_SHARE_DELETE,
system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL, allocator);
system.CREATE_ALWAYS, system.FILE_ATTRIBUTE_NORMAL);
return openHandle(handle);
} else {
unreachable;
Expand Down Expand Up @@ -521,24 +515,21 @@ pub fn OutStream(comptime Error: type) type {
};
}

/// `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 = try File.openWrite(path, allocator);
/// `path` needs to be copied in memory to add a null terminating byte, hence the allocator.
pub fn writeFile(allocator: &mem.Allocator, path: []const u8, data: []const u8) !void {
var file = try File.openWrite(allocator, path);
defer file.close();
try file.write(data);
}

/// On success, caller owns returned buffer.
pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) ![]u8 {
return readFileAllocExtra(path, allocator, 0);
pub fn readFileAlloc(allocator: &mem.Allocator, path: []const u8) ![]u8 {
return readFileAllocExtra(allocator, path, 0);
}
/// On success, caller owns returned buffer.
/// Allocates extra_len extra bytes at the end of the file buffer, which are uninitialized.
pub fn readFileAllocExtra(path: []const u8, allocator: &mem.Allocator, extra_len: usize) ![]u8 {
var file = try File.openRead(path, allocator);
pub fn readFileAllocExtra(allocator: &mem.Allocator, path: []const u8, extra_len: usize) ![]u8 {
var file = try File.openRead(allocator, path);
defer file.close();

const size = try file.getEndPos();
Expand Down
4 changes: 2 additions & 2 deletions std/io_test.zig
Expand Up @@ -13,7 +13,7 @@ test "write a file, read it, then delete it" {
rng.fillBytes(data[0..]);
const tmp_file_name = "temp_test_file.txt";
{
var file = try io.File.openWrite(tmp_file_name, allocator);
var file = try io.File.openWrite(allocator, tmp_file_name);
defer file.close();

var file_out_stream = io.FileOutStream.init(&file);
Expand All @@ -25,7 +25,7 @@ test "write a file, read it, then delete it" {
try buf_stream.flush();
}
{
var file = try io.File.openRead(tmp_file_name, allocator);
var file = try io.File.openRead(allocator, tmp_file_name);
defer file.close();

const file_size = try file.getEndPos();
Expand Down

0 comments on commit a2bd9f8

Please sign in to comment.