| ... | @@ -185,6 +185,79 @@ pub const ArgIteratorPosix = struct { | ... | @@ -185,6 +185,79 @@ pub const ArgIteratorPosix = struct { |
| 185 | } | 185 | } |
| 186 | }; | 186 | }; |
| 187 | | 187 | |
| | 188 | pub const ArgIteratorWasi = struct { |
| | 189 | allocator: *mem.Allocator, |
| | 190 | index: usize, |
| | 191 | args: [][]u8, |
| | 192 | |
| | 193 | pub const InitError = error{OutOfMemory} || os.UnexpectedError; |
| | 194 | |
| | 195 | /// You must call deinit to free the internal buffer of the |
| | 196 | /// iterator after you are done. |
| | 197 | pub fn init(allocator: *mem.Allocator) InitError!ArgIteratorWasi { |
| | 198 | const fetched_args = try ArgIteratorWasi.internalInit(allocator); |
| | 199 | return ArgIteratorWasi{ |
| | 200 | .allocator = allocator, |
| | 201 | .index = 0, |
| | 202 | .args = fetched_args, |
| | 203 | }; |
| | 204 | } |
| | 205 | |
| | 206 | fn internalInit(allocator: *mem.Allocator) InitError![][]u8 { |
| | 207 | const w = os.wasi; |
| | 208 | var count: usize = undefined; |
| | 209 | var buf_size: usize = undefined; |
| | 210 | |
| | 211 | switch (w.args_sizes_get(&count, &buf_size)) { |
| | 212 | w.ESUCCESS => {}, |
| | 213 | else => |err| return os.unexpectedErrno(err), |
| | 214 | } |
| | 215 | |
| | 216 | var argv = try allocator.alloc([*:0]u8, count); |
| | 217 | defer allocator.free(argv); |
| | 218 | |
| | 219 | var argv_buf = try allocator.alloc(u8, buf_size); |
| | 220 | |
| | 221 | switch (w.args_get(argv.ptr, argv_buf.ptr)) { |
| | 222 | w.ESUCCESS => {}, |
| | 223 | else => |err| return os.unexpectedErrno(err), |
| | 224 | } |
| | 225 | |
| | 226 | var result_args = try allocator.alloc([]u8, count); |
| | 227 | var i: usize = 0; |
| | 228 | while (i < count) : (i += 1) { |
| | 229 | result_args[i] = mem.spanZ(argv[i]); |
| | 230 | } |
| | 231 | |
| | 232 | return result_args; |
| | 233 | } |
| | 234 | |
| | 235 | pub fn next(self: *ArgIteratorWasi) ?[]const u8 { |
| | 236 | if (self.index == self.args.len) return null; |
| | 237 | |
| | 238 | const arg = self.args[self.index]; |
| | 239 | self.index += 1; |
| | 240 | return arg; |
| | 241 | } |
| | 242 | |
| | 243 | pub fn skip(self: *ArgIteratorWasi) bool { |
| | 244 | if (self.index == self.args.len) return false; |
| | 245 | |
| | 246 | self.index += 1; |
| | 247 | return true; |
| | 248 | } |
| | 249 | |
| | 250 | /// Call to free the internal buffer of the iterator. |
| | 251 | pub fn deinit(self: *ArgIteratorWasi) void { |
| | 252 | const last_item = self.args[self.args.len - 1]; |
| | 253 | const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated |
| | 254 | const first_item_ptr = self.args[0].ptr; |
| | 255 | const len = last_byte_addr - @ptrToInt(first_item_ptr); |
| | 256 | self.allocator.free(first_item_ptr[0..len]); |
| | 257 | self.allocator.free(self.args); |
| | 258 | } |
| | 259 | }; |
| | 260 | |
| 188 | pub const ArgIteratorWindows = struct { | 261 | pub const ArgIteratorWindows = struct { |
| 189 | index: usize, | 262 | index: usize, |
| 190 | cmd_line: [*]const u8, | 263 | cmd_line: [*]const u8, |
| ... | @@ -335,14 +408,29 @@ pub const ArgIteratorWindows = struct { | ... | @@ -335,14 +408,29 @@ pub const ArgIteratorWindows = struct { |
| 335 | }; | 408 | }; |
| 336 | | 409 | |
| 337 | pub const ArgIterator = struct { | 410 | pub const ArgIterator = struct { |
| 338 | const InnerType = if (builtin.os.tag == .windows) ArgIteratorWindows else ArgIteratorPosix; | 411 | const InnerType = switch (builtin.os.tag) { |
| | 412 | .windows => ArgIteratorWindows, |
| | 413 | .wasi => ArgIteratorWasi, |
| | 414 | else => ArgIteratorPosix, |
| | 415 | }; |
| 339 | | 416 | |
| 340 | inner: InnerType, | 417 | inner: InnerType, |
| 341 | | 418 | |
| | 419 | /// Initialize the args iterator. |
| 342 | pub fn init() ArgIterator { | 420 | pub fn init() ArgIterator { |
| 343 | if (builtin.os.tag == .wasi) { | 421 | if (builtin.os.tag == .wasi) { |
| 344 | // TODO: Figure out a compatible interface accomodating WASI | 422 | @compileError("In WASI, use initWithAllocator instead."); |
| 345 | @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead."); | 423 | } |
| | 424 | |
| | 425 | return ArgIterator{ .inner = InnerType.init() }; |
| | 426 | } |
| | 427 | |
| | 428 | pub const InitError = ArgIteratorWasi.InitError; |
| | 429 | |
| | 430 | /// You must deinitialize iterator's internal buffers by calling `deinit` when done. |
| | 431 | pub fn initWithAllocator(allocator: *mem.Allocator) InitError!ArgIterator { |
| | 432 | if (builtin.os.tag == .wasi) { |
| | 433 | return ArgIterator{ .inner = try InnerType.init(allocator) }; |
| 346 | } | 434 | } |
| 347 | | 435 | |
| 348 | return ArgIterator{ .inner = InnerType.init() }; | 436 | return ArgIterator{ .inner = InnerType.init() }; |
| ... | @@ -364,49 +452,62 @@ pub const ArgIterator = struct { | ... | @@ -364,49 +452,62 @@ pub const ArgIterator = struct { |
| 364 | return self.inner.next(); | 452 | return self.inner.next(); |
| 365 | } | 453 | } |
| 366 | | 454 | |
| | 455 | /// If you only are targeting WASI, you can call this and not need an allocator. |
| | 456 | pub fn nextWasi(self: *ArgIterator) ?[]const u8 { |
| | 457 | return self.inner.next(); |
| | 458 | } |
| | 459 | |
| 367 | /// Parse past 1 argument without capturing it. | 460 | /// Parse past 1 argument without capturing it. |
| 368 | /// Returns `true` if skipped an arg, `false` if we are at the end. | 461 | /// Returns `true` if skipped an arg, `false` if we are at the end. |
| 369 | pub fn skip(self: *ArgIterator) bool { | 462 | pub fn skip(self: *ArgIterator) bool { |
| 370 | return self.inner.skip(); | 463 | return self.inner.skip(); |
| 371 | } | 464 | } |
| | 465 | |
| | 466 | /// Call this to free the iterator's internal buffer if the iterator |
| | 467 | /// was created with `initWithAllocator` function. |
| | 468 | pub fn deinit(self: *ArgIterator) void { |
| | 469 | // Unless we're targeting WASI, this is a no-op. |
| | 470 | if (builtin.os.tag == .wasi) { |
| | 471 | self.inner.deinit(); |
| | 472 | } |
| | 473 | } |
| 372 | }; | 474 | }; |
| 373 | | 475 | |
| 374 | pub fn args() ArgIterator { | 476 | pub fn args() ArgIterator { |
| 375 | return ArgIterator.init(); | 477 | return ArgIterator.init(); |
| 376 | } | 478 | } |
| 377 | | 479 | |
| 378 | /// Caller must call argsFree on result. | 480 | /// You must deinitialize iterator's internal buffers by calling `deinit` when done. |
| 379 | pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { | 481 | pub fn argsWithAllocator(allocator: *mem.Allocator) ArgIterator.InitError!ArgIterator { |
| 380 | if (builtin.os.tag == .wasi) { | 482 | return ArgIterator.initWithAllocator(allocator); |
| 381 | var count: usize = undefined; | 483 | } |
| 382 | var buf_size: usize = undefined; | | |
| 383 | | | |
| 384 | const args_sizes_get_ret = os.wasi.args_sizes_get(&count, &buf_size); | | |
| 385 | if (args_sizes_get_ret != os.wasi.ESUCCESS) { | | |
| 386 | return os.unexpectedErrno(args_sizes_get_ret); | | |
| 387 | } | | |
| 388 | | | |
| 389 | var argv = try allocator.alloc([*:0]u8, count); | | |
| 390 | defer allocator.free(argv); | | |
| 391 | | 484 | |
| 392 | var argv_buf = try allocator.alloc(u8, buf_size); | 485 | test "args iterator" { |
| 393 | const args_get_ret = os.wasi.args_get(argv.ptr, argv_buf.ptr); | 486 | var ga = std.testing.allocator; |
| 394 | if (args_get_ret != os.wasi.ESUCCESS) { | 487 | var it = if (builtin.os.tag == .wasi) try argsWithAllocator(ga) else args(); |
| 395 | return os.unexpectedErrno(args_get_ret); | 488 | defer it.deinit(); // no-op unless WASI |
| 396 | } | | |
| 397 | | 489 | |
| 398 | var result_slice = try allocator.alloc([]u8, count); | 490 | const prog_name = try it.next(ga) orelse unreachable; |
| | 491 | defer ga.free(prog_name); |
| 399 | | 492 | |
| 400 | var i: usize = 0; | 493 | const expected_suffix = switch (builtin.os.tag) { |
| 401 | while (i < count) : (i += 1) { | 494 | .wasi => "test.wasm", |
| 402 | result_slice[i] = mem.spanZ(argv[i]); | 495 | .windows => "test.exe", |
| 403 | } | 496 | else => "test", |
| | 497 | }; |
| | 498 | const given_suffix = std.fs.path.basename(prog_name); |
| 404 | | 499 | |
| 405 | return result_slice; | 500 | testing.expect(mem.eql(u8, expected_suffix, given_suffix)); |
| 406 | } | 501 | testing.expectEqual(it.next(ga), null); |
| | 502 | testing.expect(!it.skip()); |
| | 503 | } |
| 407 | | 504 | |
| | 505 | /// Caller must call argsFree on result. |
| | 506 | pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { |
| 408 | // TODO refactor to only make 1 allocation. | 507 | // TODO refactor to only make 1 allocation. |
| 409 | var it = args(); | 508 | var it = if (builtin.os.tag == .wasi) try argsWithAllocator(allocator) else args(); |
| | 509 | defer it.deinit(); |
| | 510 | |
| 410 | var contents = std.ArrayList(u8).init(allocator); | 511 | var contents = std.ArrayList(u8).init(allocator); |
| 411 | defer contents.deinit(); | 512 | defer contents.deinit(); |
| 412 | | 513 | |
| ... | @@ -442,16 +543,6 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { | ... | @@ -442,16 +543,6 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![][]u8 { |
| 442 | } | 543 | } |
| 443 | | 544 | |
| 444 | pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { | 545 | pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { |
| 445 | if (builtin.os.tag == .wasi) { | | |
| 446 | const last_item = args_alloc[args_alloc.len - 1]; | | |
| 447 | const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated | | |
| 448 | const first_item_ptr = args_alloc[0].ptr; | | |
| 449 | const len = last_byte_addr - @ptrToInt(first_item_ptr); | | |
| 450 | allocator.free(first_item_ptr[0..len]); | | |
| 451 | | | |
| 452 | return allocator.free(args_alloc); | | |
| 453 | } | | |
| 454 | | | |
| 455 | var total_bytes: usize = 0; | 546 | var total_bytes: usize = 0; |
| 456 | for (args_alloc) |arg| { | 547 | for (args_alloc) |arg| { |
| 457 | total_bytes += @sizeOf([]u8) + arg.len; | 548 | total_bytes += @sizeOf([]u8) + arg.len; |