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 {
465465
466466pub const ToSliceError = Iterator.Windows.InitError || Iterator.Wasi.InitError;
467467
468/// Returned value may reference several allocations; call `freeSlice` to
469/// release.
468/// Returned value may reference several allocations and may point into `a`.
469/// Thefore, an arena-style allocator must be used.
470470///
471471/// * On Windows, the result is encoded as
472472/// [WTF-8](https://wtf-8.codeberg.page/).
473473/// * On other platforms, the result is an opaque sequence of bytes with no
474474/// particular encoding.
475pub fn toSlice(a: Args, gpa: Allocator) ToSliceError![][:0]u8 {
476 var it = try a.iterateAllocator(gpa);
477 defer it.deinit();
475///
476/// See also:
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;
480 defer contents.deinit(gpa);
506 return result_slice_list;
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;
483 defer slice_list.deinit(gpa);
511 switch (std.os.wasi.args_sizes_get(&count, &buf_size)) {
512 .SUCCESS => {},
513 else => |err| return std.posix.unexpectedErrno(err),
514 }
484515
485 while (it.next()) |arg| {
486 try contents.appendSlice(gpa, arg[0 .. arg.len + 1]);
487 try slice_list.append(gpa, arg.len);
488 }
516 if (count == 0) return &.{};
489517
490 const contents_slice = contents.items;
491 const slice_sizes = slice_list.items;
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 }
518 const argv = try arena.alloc([*:0]u8, count);
519 const argv_buf = try arena.alloc(u8, buf_size);
507520
508 return result_slice_list;
509}
521 switch (std.os.wasi.args_get(argv.ptr, argv_buf.ptr)) {
522 .SUCCESS => {},
523 else => |err| return std.posix.unexpectedErrno(err),
524 }
510525
511/// Frees memory allocate by `toSlice`.
512pub fn freeSlice(gpa: Allocator, to_slice_result: []const [:0]u8) void {
513 var total_bytes: usize = 0;
514 for (to_slice_result) |arg| {
515 total_bytes += @sizeOf([]u8) + arg.len + 1;
526 const args = try arena.alloc([:0]const u8, count);
527 for (args, argv) |*dst, src| dst.* = std.mem.sliceTo(src, 0);
528 return args;
529 } else {
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;
516533 }
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);
520534}
521535
522536test "Iterator.Windows" {