authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-01 18:57:13-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-04 00:27:08-08:00
loga6f519c20f105a60c2ac51530354f9ac7c3e1fd9
tree386c78c89196b082944ac36f3f1f4b7b02aeb5bb
parent1ccc87363a6436da293dbb1bc7bc8db2aa4d7bf7

std.process.Args: make toSlice require arena allocation

this simplifies the implementation, allows specializing it, and allows deleting the corresponding free function. In practice this is how it is always used anyway.

1 files changed, 54 insertions(+), 40 deletions(-)

lib/std/process/Args.zig+54-40
...@@ -465,58 +465,72 @@ pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator {...@@ -465,58 +465,72 @@ pub fn iterateAllocator(a: Args, gpa: Allocator) Iterator.InitError!Iterator {
465465
466pub const ToSliceError = Iterator.Windows.InitError || Iterator.Wasi.InitError;466pub const ToSliceError = Iterator.Windows.InitError || Iterator.Wasi.InitError;
467467
468/// Returned value may reference several allocations; call `freeSlice` to468/// Returned value may reference several allocations and may point into `a`.
469/// release.469/// Thefore, an arena-style allocator must be used.
470///470///
471/// * On Windows, the result is encoded as471/// * On Windows, the result is encoded as
472/// [WTF-8](https://wtf-8.codeberg.page/).472/// [WTF-8](https://wtf-8.codeberg.page/).
473/// * On other platforms, the result is an opaque sequence of bytes with no473/// * On other platforms, the result is an opaque sequence of bytes with no
474/// particular encoding.474/// particular encoding.
475pub fn toSlice(a: Args, gpa: Allocator) ToSliceError![][:0]u8 {475///
476 var it = try a.iterateAllocator(gpa);476/// See also:
477 defer it.deinit();477/// * `iterate`
478/// * `iterateAllocator`
479pub fn toSlice(a: Args, arena: Allocator) ToSliceError![]const [:0]const u8 {
480 if (native_os == .windows) {
481 var it = try a.iterateAllocator(arena);
482 var contents: std.ArrayList(u8) = .empty;
483 var slice_list: std.ArrayList(usize) = .empty;
484 while (it.next()) |arg| {
485 try contents.appendSlice(arena, arg[0 .. arg.len + 1]);
486 try slice_list.append(arena, arg.len);
487 }
488 const contents_slice = contents.items;
489 const slice_sizes = slice_list.items;
490 const slice_list_bytes = std.math.mul(usize, @sizeOf([]u8), slice_sizes.len) catch return error.OutOfMemory;
491 const total_bytes = std.math.add(usize, slice_list_bytes, contents_slice.len) catch return error.OutOfMemory;
492 const buf = try arena.alignedAlloc(u8, .of([]u8), total_bytes);
493 errdefer arena.free(buf);
494
495 const result_slice_list = std.mem.bytesAsSlice([:0]u8, buf[0..slice_list_bytes]);
496 const result_contents = buf[slice_list_bytes..];
497 @memcpy(result_contents[0..contents_slice.len], contents_slice);
498
499 var contents_index: usize = 0;
500 for (slice_sizes, 0..) |len, i| {
501 const new_index = contents_index + len;
502 result_slice_list[i] = result_contents[contents_index..new_index :0];
503 contents_index = new_index + 1;
504 }
478505
479 var contents: std.ArrayList(u8) = .empty;506 return result_slice_list;
480 defer contents.deinit(gpa);507 } else if (native_os == .wasi and !builtin.link_libc) {
508 var count: usize = undefined;
509 var buf_size: usize = undefined;
481510
482 var slice_list: std.ArrayList(usize) = .empty;511 switch (std.os.wasi.args_sizes_get(&count, &buf_size)) {
483 defer slice_list.deinit(gpa);512 .SUCCESS => {},
513 else => |err| return std.posix.unexpectedErrno(err),
514 }
484515
485 while (it.next()) |arg| {516 if (count == 0) return &.{};
486 try contents.appendSlice(gpa, arg[0 .. arg.len + 1]);
487 try slice_list.append(gpa, arg.len);
488 }
489517
490 const contents_slice = contents.items;518 const argv = try arena.alloc([*:0]u8, count);
491 const slice_sizes = slice_list.items;519 const argv_buf = try arena.alloc(u8, buf_size);
492 const slice_list_bytes = std.math.mul(usize, @sizeOf([]u8), slice_sizes.len) catch return error.OutOfMemory;
493 const total_bytes = std.math.add(usize, slice_list_bytes, contents_slice.len) catch return error.OutOfMemory;
494 const buf = try gpa.alignedAlloc(u8, .of([]u8), total_bytes);
495 errdefer gpa.free(buf);
496
497 const result_slice_list = std.mem.bytesAsSlice([:0]u8, buf[0..slice_list_bytes]);
498 const result_contents = buf[slice_list_bytes..];
499 @memcpy(result_contents[0..contents_slice.len], contents_slice);
500
501 var contents_index: usize = 0;
502 for (slice_sizes, 0..) |len, i| {
503 const new_index = contents_index + len;
504 result_slice_list[i] = result_contents[contents_index..new_index :0];
505 contents_index = new_index + 1;
506 }
507520
508 return result_slice_list;521 switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) {
509}522 .SUCCESS => {},
523 else => |err| return std.posix.unexpectedErrno(err),
524 }
510525
511/// Frees memory allocate by `toSlice`.526 const args = try arena.alloc([:0]const u8, count);
512pub fn freeSlice(gpa: Allocator, to_slice_result: []const [:0]u8) void {527 for (args, argv) |*dst, src| dst.* = std.mem.sliceTo(src, 0);
513 var total_bytes: usize = 0;528 return args;
514 for (to_slice_result) |arg| {529 } else {
515 total_bytes += @sizeOf([]u8) + arg.len + 1;530 const args = try arena.alloc([:0]const u8, a.vector.len);
531 for (args, a.vector) |*dst, src| dst.* = std.mem.sliceTo(src, 0);
532 return args;
516 }533 }
517 const unaligned_allocated_buf = @as([*]const u8, @ptrCast(to_slice_result.ptr))[0..total_bytes];
518 const aligned_allocated_buf: []align(@alignOf([]u8)) const u8 = @alignCast(unaligned_allocated_buf);
519 return gpa.free(aligned_allocated_buf);
520}534}
521535
522test "Iterator.Windows" {536test "Iterator.Windows" {