mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
Add ArgIteratorWasi and integrate it with ArgIterator
This commit pulls WASI specific implementation of args extraction from the runtime from `process.argsAlloc` and `process.argsFree` into a new iterator struct `process.ArgIteratorWasi`. It also integrates the struct with platform-independent `process.ArgIterator`.
This commit is contained in:
parent
dc4fea983d
commit
f1a4e1a70f
@ -185,6 +185,91 @@ pub const ArgIteratorPosix = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const ArgIteratorWasi = struct {
|
||||
allocator: *mem.Allocator,
|
||||
index: usize,
|
||||
args: [][]u8,
|
||||
|
||||
pub const InitError = error{OutOfMemory} || os.UnexpectedError;
|
||||
|
||||
/// You must call deinit to free the internal buffer of the
|
||||
/// iterator after you are done.
|
||||
pub fn init(allocator: *mem.Allocator) InitError!ArgIteratorWasi {
|
||||
const fetched_args = try ArgIteratorWasi.internalInit(allocator);
|
||||
return ArgIteratorWasi{
|
||||
.allocator = allocator,
|
||||
.index = 0,
|
||||
.args = fetched_args,
|
||||
};
|
||||
}
|
||||
|
||||
fn internalInit(allocator: *mem.Allocator) InitError![][]u8 {
|
||||
const w = os.wasi;
|
||||
var count: usize = undefined;
|
||||
var buf_size: usize = undefined;
|
||||
|
||||
switch (w.args_sizes_get(&count, &buf_size)) {
|
||||
w.ESUCCESS => {},
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
|
||||
var argv = try allocator.alloc([*:0]u8, count);
|
||||
defer allocator.free(argv);
|
||||
|
||||
var argv_buf = try allocator.alloc(u8, buf_size);
|
||||
|
||||
switch (w.args_get(argv.ptr, argv_buf.ptr)) {
|
||||
w.ESUCCESS => {},
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
|
||||
var result_args = try allocator.alloc([]u8, count);
|
||||
var i: usize = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
result_args[i] = mem.spanZ(argv[i]);
|
||||
}
|
||||
|
||||
return result_args;
|
||||
}
|
||||
|
||||
pub fn next(self: *ArgIteratorWasi) ?[]const u8 {
|
||||
if (self.index == self.args.len) return null;
|
||||
|
||||
const arg = self.args[self.index];
|
||||
self.index += 1;
|
||||
return arg;
|
||||
}
|
||||
|
||||
pub fn skip(self: *ArgIteratorWasi) bool {
|
||||
if (self.index == self.args.len) return false;
|
||||
|
||||
self.index += 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Call to free the internal buffer of the iterator.
|
||||
pub fn deinit(self: *ArgIteratorWasi) void {
|
||||
const last_item = self.args[self.args.len - 1];
|
||||
const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated
|
||||
const first_item_ptr = self.args[0].ptr;
|
||||
const len = last_byte_addr - @ptrToInt(first_item_ptr);
|
||||
self.allocator.free(first_item_ptr[0..len]);
|
||||
self.allocator.free(self.args);
|
||||
}
|
||||
};
|
||||
|
||||
test "process.ArgIteratorWasi" {
|
||||
if (builtin.os.tag != .wasi) return error.SkipZigTest;
|
||||
|
||||
var ga = std.testing.allocator;
|
||||
var args_it = try ArgIteratorWasi.init(ga);
|
||||
defer args_it.deinit();
|
||||
|
||||
testing.expectEqual(@as(usize, 1), args_it.args.len);
|
||||
const prog_name = args_it.next() orelse unreachable;
|
||||
testing.expect(mem.eql(u8, "test.wasm", prog_name));
|
||||
}
|
||||
|
||||
pub const ArgIteratorWindows = struct {
|
||||
index: usize,
|
||||
cmd_line: [*]const u8,
|
||||
@ -335,19 +420,37 @@ pub const ArgIteratorWindows = struct {
|
||||
};
|
||||
|
||||
pub const ArgIterator = struct {
|
||||
const InnerType = if (builtin.os.tag == .windows) ArgIteratorWindows else ArgIteratorPosix;
|
||||
const InnerType = switch (builtin.os.tag) {
|
||||
.windows => ArgIteratorWindows,
|
||||
.wasi => ArgIteratorWasi,
|
||||
else => ArgIteratorPosix,
|
||||
};
|
||||
|
||||
inner: InnerType,
|
||||
|
||||
/// Initialize the args iterator.
|
||||
///
|
||||
/// On WASI, will panic if the default Wasm page allocator runs out of memory
|
||||
/// or there is an error fetching the args from the runtime. If you want to
|
||||
/// use custom allocator and handle the errors yourself, call `initWasi()` instead.
|
||||
/// You also must remember to free the buffer with `deinitWasi()` call.
|
||||
pub fn init() ArgIterator {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
// TODO: Figure out a compatible interface accomodating WASI
|
||||
@compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead.");
|
||||
const allocator = std.heap.page_allocator;
|
||||
return ArgIterator.initWasi(allocator) catch @panic("unexpected error occurred when initializing ArgIterator");
|
||||
}
|
||||
|
||||
return ArgIterator{ .inner = InnerType.init() };
|
||||
}
|
||||
|
||||
pub const InitError = ArgIteratorWasi.InitError;
|
||||
|
||||
/// If you are targeting WASI, you can call this to manually specify the allocator and
|
||||
/// handle any errors.
|
||||
pub fn initWasi(allocator: *mem.Allocator) InitError!ArgIterator {
|
||||
return ArgIterator{ .inner = try InnerType.init(allocator) };
|
||||
}
|
||||
|
||||
pub const NextError = ArgIteratorWindows.NextError;
|
||||
|
||||
/// You must free the returned memory when done.
|
||||
@ -364,11 +467,22 @@ pub const ArgIterator = struct {
|
||||
return self.inner.next();
|
||||
}
|
||||
|
||||
/// If you only are targeting WASI, you can call this and not need an allocator.
|
||||
pub fn nextWasi(self: *ArgIterator) ?[]const u8 {
|
||||
return self.inner.next();
|
||||
}
|
||||
|
||||
/// Parse past 1 argument without capturing it.
|
||||
/// Returns `true` if skipped an arg, `false` if we are at the end.
|
||||
pub fn skip(self: *ArgIterator) bool {
|
||||
return self.inner.skip();
|
||||
}
|
||||
|
||||
/// If you are targeting WASI, call this to free the iterator's internal buffer
|
||||
/// after you are done with it.
|
||||
pub fn deinitWasi(self: *ArgIterator) void {
|
||||
self.inner.deinit();
|
||||
}
|
||||
};
|
||||
|
||||
pub fn args() ArgIterator {
|
||||
@ -377,36 +491,10 @@ pub fn args() ArgIterator {
|
||||
|
||||
/// Caller must call argsFree on result.
|
||||
pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
var count: usize = undefined;
|
||||
var buf_size: usize = undefined;
|
||||
|
||||
const args_sizes_get_ret = os.wasi.args_sizes_get(&count, &buf_size);
|
||||
if (args_sizes_get_ret != os.wasi.ESUCCESS) {
|
||||
return os.unexpectedErrno(args_sizes_get_ret);
|
||||
}
|
||||
|
||||
var argv = try allocator.alloc([*:0]u8, count);
|
||||
defer allocator.free(argv);
|
||||
|
||||
var argv_buf = try allocator.alloc(u8, buf_size);
|
||||
const args_get_ret = os.wasi.args_get(argv.ptr, argv_buf.ptr);
|
||||
if (args_get_ret != os.wasi.ESUCCESS) {
|
||||
return os.unexpectedErrno(args_get_ret);
|
||||
}
|
||||
|
||||
var result_slice = try allocator.alloc([]u8, count);
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < count) : (i += 1) {
|
||||
result_slice[i] = mem.spanZ(argv[i]);
|
||||
}
|
||||
|
||||
return result_slice;
|
||||
}
|
||||
|
||||
// TODO refactor to only make 1 allocation.
|
||||
var it = args();
|
||||
defer if (builtin.os.tag == .wasi) it.deinitWasi();
|
||||
|
||||
var contents = std.ArrayList(u8).init(allocator);
|
||||
defer contents.deinit();
|
||||
|
||||
@ -442,16 +530,6 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 {
|
||||
}
|
||||
|
||||
pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
const last_item = args_alloc[args_alloc.len - 1];
|
||||
const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated
|
||||
const first_item_ptr = args_alloc[0].ptr;
|
||||
const len = last_byte_addr - @ptrToInt(first_item_ptr);
|
||||
allocator.free(first_item_ptr[0..len]);
|
||||
|
||||
return allocator.free(args_alloc);
|
||||
}
|
||||
|
||||
var total_bytes: usize = 0;
|
||||
for (args_alloc) |arg| {
|
||||
total_bytes += @sizeOf([]u8) + arg.len;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user