diff --git a/lib/c/string.zig b/lib/c/string.zig index a644093af57e..a24cb633b2f2 100644 --- a/lib/c/string.zig +++ b/lib/c/string.zig @@ -15,7 +15,7 @@ comptime { fn strcmp(s1: [*:0]const c_char, s2: [*:0]const c_char) callconv(.c) c_int { // We need to perform unsigned comparisons. - return switch (std.mem.orderZ(u8, @ptrCast(s1), @ptrCast(s2))) { + return switch (std.mem.orderSentinel(u8, 0, @ptrCast(s1), @ptrCast(s2))) { .lt => -1, .eq => 0, .gt => 1, diff --git a/lib/compiler/aro/aro/CodeGen.zig b/lib/compiler/aro/aro/CodeGen.zig index 9145d38a8805..585dcc5d4c30 100644 --- a/lib/compiler/aro/aro/CodeGen.zig +++ b/lib/compiler/aro/aro/CodeGen.zig @@ -890,7 +890,7 @@ fn genLval(c: *CodeGen, node_index: Node.Index) Error!Ir.Ref { } } - const duped_name = try c.builder.arena.allocator().dupeZ(u8, slice); + const duped_name = try c.builder.arena.allocator().dupeSentinel(u8, slice, 0); const ref: Ir.Ref = @enumFromInt(c.builder.instructions.len); try c.builder.instructions.append(c.builder.gpa, .{ .tag = .symbol, .data = .{ .label = duped_name }, .ty = .ptr }); return ref; @@ -1108,7 +1108,7 @@ fn genCall(c: *CodeGen, call: Node.Call) Error!Ir.Ref { } } - const duped_name = try c.builder.arena.allocator().dupeZ(u8, slice); + const duped_name = try c.builder.arena.allocator().dupeSentinel(u8, slice, 0); const ref: Ir.Ref = @enumFromInt(c.builder.instructions.len); try c.builder.instructions.append(c.builder.gpa, .{ .tag = .symbol, .data = .{ .label = duped_name }, .ty = .ptr }); break :blk ref; diff --git a/lib/std/Build/Watch/FsEvents.zig b/lib/std/Build/Watch/FsEvents.zig index c8c53813c2fb..d9c3e29f5fc0 100644 --- a/lib/std/Build/Watch/FsEvents.zig +++ b/lib/std/Build/Watch/FsEvents.zig @@ -193,7 +193,7 @@ pub fn setPaths(fse: *FsEvents, gpa: Allocator, steps: []const *std.Build.Step) } else { fse.watch_roots = try gpa.realloc(fse.watch_roots, need_dirs.count()); for (fse.watch_roots, need_dirs.keys()) |*out, in| { - out.* = try paths_arena.dupeZ(u8, in); + out.* = try paths_arena.dupeSentinel(u8, in, 0); } } if (enable_debug_logs) { diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 2ae9a23f85da..374473a1115f 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -523,7 +523,7 @@ pub fn defaultPanic( if (uefi.system_table.boot_services) |bs| { // ExitData buffer must be allocated using boot_services.allocatePool (spec: page 220) - const exit_data = uefi.raw_pool_allocator.dupeZ(u16, exit_msg) catch @trap(); + const exit_data = uefi.raw_pool_allocator.dupeSentinel(u16, exit_msg, 0) catch @trap(); bs.exit(uefi.handle, .aborted, exit_data) catch {}; } @trap(); diff --git a/lib/std/fs/get_app_data_dir.zig b/lib/std/fs/get_app_data_dir.zig index a256758a07e2..5b0c0d79271c 100644 --- a/lib/std/fs/get_app_data_dir.zig +++ b/lib/std/fs/get_app_data_dir.zig @@ -46,7 +46,7 @@ pub fn getAppDataDir(allocator: mem.Allocator, appname: []const u8) GetAppDataDi .haiku => { var dir_path_buf: [std.fs.max_path_bytes]u8 = undefined; const rc = std.c.find_directory(.B_USER_SETTINGS_DIRECTORY, -1, true, &dir_path_buf, dir_path_buf.len); - const settings_dir = try allocator.dupeZ(u8, mem.sliceTo(&dir_path_buf, 0)); + const settings_dir = try allocator.dupeSentinel(u8, mem.sliceTo(&dir_path_buf, 0), 0); defer allocator.free(settings_dir); switch (rc) { 0 => return fs.path.join(allocator, &[_][]const u8{ settings_dir, appname }), diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 0949d71a2da3..1131adbb95e4 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -105,7 +105,7 @@ const TestContext = struct { const allocator = self.arena.allocator(); const transformed_path = try self.transform_fn(allocator, self.dir, relative_path); if (native_os == .windows) { - const transformed_sep_path = try allocator.dupeZ(u8, transformed_path); + const transformed_sep_path = try allocator.dupeSentinel(u8, transformed_path, 0); std.mem.replaceScalar(u8, transformed_sep_path, switch (self.path_sep) { '/' => '\\', '\\' => '/', @@ -123,7 +123,7 @@ const TestContext = struct { pub fn toCanonicalPathSep(self: *TestContext, path: [:0]const u8) ![:0]const u8 { if (native_os == .windows) { const allocator = self.arena.allocator(); - const transformed_sep_path = try allocator.dupeZ(u8, path); + const transformed_sep_path = try allocator.dupeSentinel(u8, path, 0); std.mem.replaceScalar(u8, transformed_sep_path, '/', '\\'); return transformed_sep_path; } diff --git a/lib/std/mem.zig b/lib/std/mem.zig index ed48132d9a67..55684e6decd5 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -658,11 +658,21 @@ pub fn order(comptime T: type, lhs: []const T, rhs: []const T) math.Order { return math.order(lhs.len, rhs.len); } -/// Compares two many-item pointers with NUL-termination lexicographically. -pub fn orderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T) math.Order { +/// Compares two many-item pointers with sentinel-termination lexicographically. +pub fn orderSentinel(comptime T: type, comptime s: T, lhs: [*:s]const T, rhs: [*:s]const T) math.Order { var i: usize = 0; - while (lhs[i] == rhs[i] and lhs[i] != 0) : (i += 1) {} - return math.order(lhs[i], rhs[i]); + while (lhs[i] == rhs[i] and lhs[i] != s) : (i += 1) {} + if (lhs[i] == s) { + return if (rhs[i] == s) .eq else .lt; + } else if (rhs[i] == s) { + return .gt; + } else { + return math.order(lhs[i], rhs[i]); + } +} +/// Deprecated in favor of `orderSentinel`. +pub fn orderZ(comptime T: type, lhs: [*:0]const T, rhs: [*:0]const T) math.Order { + return orderSentinel(T, 0, lhs, rhs); } test order { @@ -671,8 +681,20 @@ test order { try testing.expect(order(u8, "abc", "abc0") == .lt); try testing.expect(order(u8, "", "") == .eq); try testing.expect(order(u8, "", "a") == .lt); + try testing.expect(order(i8, &.{ -1, -2 }, &.{ -1, -2, -3 }) == .lt); } +test orderSentinel { + try testing.expect(orderSentinel(u8, 0, "abcd", "bee") == .lt); + try testing.expect(orderSentinel(u8, 0, "abc", "abc") == .eq); + try testing.expect(orderSentinel(u8, 0, "abc", "abc0") == .lt); + try testing.expect(orderSentinel(u8, 0, "", "") == .eq); + try testing.expect(orderSentinel(u8, 0, "", "a") == .lt); + try testing.expect(orderSentinel(i8, 0, &.{ -1, -2 }, &.{ -1, -2, -3 }) == .lt); + try testing.expect(orderSentinel(u4, 4, &.{ 1, 2, 3 }, &.{ 1, 2, 3 }) == .eq); + try testing.expect(orderSentinel(u4, 4, &.{ 1, 2, 3 }, &.{ 1, 2 }) == .gt); + try testing.expect(orderSentinel(u4, 4, &.{ 1, 2, 3 }, &.{ 1, 2, 3, 5 }) == .lt); +} test orderZ { try testing.expect(orderZ(u8, "abcd", "bee") == .lt); try testing.expect(orderZ(u8, "abc", "abc") == .eq); @@ -4896,7 +4918,7 @@ test isAligned { } test "freeing empty string with null-terminated sentinel" { - const empty_string = try testing.allocator.dupeZ(u8, ""); + const empty_string = try testing.allocator.dupeSentinel(u8, "", 0); testing.allocator.free(empty_string); } diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 1d1653d3a0c9..461a90490e7d 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -451,12 +451,16 @@ pub fn dupe(allocator: Allocator, comptime T: type, m: []const T) Error![]T { return new_buf; } -/// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory. -pub fn dupeZ(allocator: Allocator, comptime T: type, m: []const T) Error![:0]T { +/// Copies `m` to newly allocated memory, with a sentinel-terminated element. Caller owns the memory. +pub fn dupeSentinel(allocator: Allocator, comptime T: type, m: []const T, comptime s: T) Error![:s]T { const new_buf = try allocator.alloc(T, m.len + 1); @memcpy(new_buf[0..m.len], m); - new_buf[m.len] = 0; - return new_buf[0..m.len :0]; + new_buf[m.len] = s; + return new_buf[0..m.len :s]; +} +/// Deprecated in favor of `dupeSentinel` +pub fn dupeZ(allocator: Allocator, comptime T: type, m: []const T) Error![:0]T { + return try dupeSentinel(allocator, T, m, 0); } /// An allocator that always fails to allocate. diff --git a/lib/std/net.zig b/lib/std/net.zig index 9d70515c2ee1..54a6038f1f60 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -910,7 +910,7 @@ pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressLis errdefer result.deinit(); if (native_os == .windows) { - const name_c = try gpa.dupeZ(u8, name); + const name_c = try gpa.dupeSentinel(u8, name, 0); defer gpa.free(name_c); const port_c = try std.fmt.allocPrintSentinel(gpa, "{d}", .{port}, 0); @@ -982,7 +982,7 @@ pub fn getAddressList(gpa: Allocator, name: []const u8, port: u16) GetAddressLis } if (builtin.link_libc) { - const name_c = try gpa.dupeZ(u8, name); + const name_c = try gpa.dupeSentinel(u8, name, 0); defer gpa.free(name_c); const port_c = try std.fmt.allocPrintSentinel(gpa, "{d}", .{port}, 0); diff --git a/lib/std/os/plan9.zig b/lib/std/os/plan9.zig index b910e26c7134..4464095db3d0 100644 --- a/lib/std/os/plan9.zig +++ b/lib/std/os/plan9.zig @@ -297,7 +297,7 @@ pub fn openat(dirfd: i32, path: [*:0]const u8, flags: u32, _: mode_t) usize { const dir_path = std.mem.span(@as([*:0]u8, @ptrCast(&dir_path_buf))); const total_path = std.fs.path.join(alloc, &.{ dir_path, std.mem.span(path) }) catch unreachable; // the allocation shouldn't fail because it should not exceed max_path_bytes fba.reset(); - const total_path_z = alloc.dupeZ(u8, total_path) catch unreachable; // should not exceed max_path_bytes + 1 + const total_path_z = alloc.dupeSentinel(u8, total_path, 0) catch unreachable; // should not exceed max_path_bytes + 1 return open(total_path_z.ptr, flags); } diff --git a/lib/std/process.zig b/lib/std/process.zig index 10025a5ea5b8..4260a4140c00 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1721,7 +1721,7 @@ pub fn execve( const arena = arena_allocator.allocator(); const argv_buf = try arena.allocSentinel(?[*:0]const u8, argv.len, null); - for (argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; + for (argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr; const envp = m: { if (env_map) |m| { @@ -1999,7 +1999,7 @@ pub fn createEnvironFromExisting( }, .nothing => {}, }; - envp_buf[i] = try arena.dupeZ(u8, mem.span(line)); + envp_buf[i] = try arena.dupeSentinel(u8, mem.span(line), 0); i += 1; } diff --git a/lib/std/process/Child.zig b/lib/std/process/Child.zig index 02d7fd8d5498..d925ffa87857 100644 --- a/lib/std/process/Child.zig +++ b/lib/std/process/Child.zig @@ -595,7 +595,7 @@ fn spawnPosix(self: *ChildProcess) SpawnError!void { // Therefore, we do all the allocation for the execve() before the fork(). // This means we must do the null-termination of argv and env vars here. const argv_buf = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null); - for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; + for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr; const prog_fileno = 3; comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno); diff --git a/lib/std/zig/LibCInstallation.zig b/lib/std/zig/LibCInstallation.zig index 0414d32ee7d6..782947ff4e1b 100644 --- a/lib/std/zig/LibCInstallation.zig +++ b/lib/std/zig/LibCInstallation.zig @@ -58,7 +58,7 @@ pub fn parse( if (value.len == 0) { @field(self, field.name) = null; } else { - found_keys[i].allocated = try allocator.dupeZ(u8, value); + found_keys[i].allocated = try allocator.dupeSentinel(u8, value, 0); @field(self, field.name) = found_keys[i].allocated; } break; @@ -197,16 +197,16 @@ pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation { } else if (is_haiku) { try self.findNativeIncludeDirPosix(args); try self.findNativeGccDirHaiku(args); - self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib"); + self.crt_dir = try args.allocator.dupeSentinel(u8, "/system/develop/lib", 0); } else if (builtin.target.os.tag.isSolarish()) { // There is only one libc, and its headers/libraries are always in the same spot. - self.include_dir = try args.allocator.dupeZ(u8, "/usr/include"); - self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include"); - self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64"); + self.include_dir = try args.allocator.dupeSentinel(u8, "/usr/include", 0); + self.sys_include_dir = try args.allocator.dupeSentinel(u8, "/usr/include", 0); + self.crt_dir = try args.allocator.dupeSentinel(u8, "/usr/lib/64", 0); } else if (std.process.can_spawn) { try self.findNativeIncludeDirPosix(args); switch (builtin.target.os.tag) { - .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"), + .freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeSentinel(u8, "/usr/lib", 0), .linux => try self.findNativeCrtDirPosix(args), else => {}, } @@ -330,7 +330,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) F if (self.include_dir == null) { if (search_dir.accessZ(include_dir_example_file, .{})) |_| { - self.include_dir = try allocator.dupeZ(u8, search_path); + self.include_dir = try allocator.dupeSentinel(u8, search_path, 0); } else |err| switch (err) { error.FileNotFound => {}, else => return error.FileSystem, @@ -339,7 +339,7 @@ fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) F if (self.sys_include_dir == null) { if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| { - self.sys_include_dir = try allocator.dupeZ(u8, search_path); + self.sys_include_dir = try allocator.dupeSentinel(u8, search_path, 0); } else |err| switch (err) { error.FileNotFound => {}, else => return error.FileSystem, @@ -622,10 +622,10 @@ fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 { // So we detect failure by checking if the output matches exactly the input. if (std.mem.eql(u8, line, args.search_basename)) return error.LibCRuntimeNotFound; switch (args.want_dirname) { - .full_path => return allocator.dupeZ(u8, line), + .full_path => return allocator.dupeSentinel(u8, line, 0), .only_dir => { const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; - return allocator.dupeZ(u8, dirname); + return allocator.dupeSentinel(u8, dirname, 0); }, } } diff --git a/src/Compilation.zig b/src/Compilation.zig index 7c5f699d8353..fb93b9870289 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1933,7 +1933,7 @@ pub fn create(gpa: Allocator, arena: Allocator, diag: *CreateDiagnostic, options const comp: *Compilation = comp: { // We put the `Compilation` itself in the arena. Freeing the arena will free the module. // It's initialized later after we prepare the initialization options. - const root_name = try arena.dupeZ(u8, options.root_name); + const root_name = try arena.dupeSentinel(u8, options.root_name, 0); // The "any" values provided by resolved config only account for // explicitly-provided settings. We now make them additionally account diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 20e5696c15a1..e66cab515d19 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -938,7 +938,7 @@ pub const Object = struct { } const target_triple_sentinel = - try o.gpa.dupeZ(u8, o.builder.target_triple.slice(&o.builder).?); + try o.gpa.dupeSentinel(u8, o.builder.target_triple.slice(&o.builder).?, 0); defer o.gpa.free(target_triple_sentinel); const emit_asm_msg = options.asm_path orelse "(none)"; diff --git a/src/link/Lld.zig b/src/link/Lld.zig index 2a3310384bc7..7938a37cc14e 100644 --- a/src/link/Lld.zig +++ b/src/link/Lld.zig @@ -286,7 +286,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { const comp = base.comp; const directory = base.emit.root_dir; // Just an alias to make it shorter to type. const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); - const full_out_path_z = try arena.dupeZ(u8, full_out_path); + const full_out_path_z = try arena.dupeSentinel(u8, full_out_path, 0); const opt_zcu = comp.zcu; const zcu_obj_path: ?Cache.Path = if (opt_zcu != null) p: { @@ -325,7 +325,7 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void { object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena)); } for (comp.win32_resource_table.keys()) |key| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); + object_files.appendAssumeCapacity(try arena.dupeSentinel(u8, key.status.success.res_path, 0)); } if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); diff --git a/src/main.zig b/src/main.zig index 89a552de0b1b..848d7f2150a4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4471,7 +4471,7 @@ fn runOrTestHotSwap( const arena = arena_allocator.allocator(); const argv_buf = try arena.allocSentinel(?[*:0]u8, argv.items.len, null); - for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; + for (argv.items, 0..) |arg, i| argv_buf[i] = (try arena.dupeSentinel(u8, arg, 0)).ptr; const pid = try PosixSpawn.spawn(argv.items[0], null, attr, argv_buf, std.c.environ); return pid; @@ -5600,7 +5600,7 @@ extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; fn argsCopyZ(alloc: Allocator, args: []const []const u8) ![:null]?[*:0]u8 { var argv = try alloc.allocSentinel(?[*:0]u8, args.len, null); for (args, 0..) |arg, i| { - argv[i] = try alloc.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. + argv[i] = try alloc.dupeSentinel(u8, arg, 0); // TODO If there was an argsAllocZ we could avoid this allocation. } return argv; } @@ -6322,7 +6322,7 @@ fn cmdDumpLlvmInts( if (!build_options.have_llvm) fatal("compiler does not use LLVM; cannot dump LLVM integer sizes", .{}); - const triple = try arena.dupeZ(u8, args[0]); + const triple = try arena.dupeSentinel(u8, args[0], 0); const llvm = @import("codegen/llvm/bindings.zig");