From c0e3fa32a9a053b9dac6455197073e200c1800eb Mon Sep 17 00:00:00 2001 From: Rue04 Date: Thu, 27 Nov 2025 22:22:23 +0100 Subject: [PATCH] `std.mem.Allocator`: add `dupeSentinel` Here, because `dupeZ` is used a lot for null-terminated strings, I believe it should stay. However, generic sentinel termination would still be useful for, as I did in the test, making a `null`-terminated slice of pointers. Could be useful especially for interacting with c code. --- lib/std/mem.zig | 12 +++++++++--- lib/std/mem/Allocator.zig | 13 +++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 0d260ccdaa..a9b8c669b2 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -4898,9 +4898,15 @@ test isAligned { try testing.expect(!isAligned(4, 16)); } -test "freeing empty string with null-terminated sentinel" { - const empty_string = try testing.allocator.dupeZ(u8, ""); - testing.allocator.free(empty_string); +test "freeing empty slices with sentinel termination" { + const gpa = testing.allocator; + + const empty_string = try gpa.dupeZ(u8, ""); + gpa.free(empty_string); + const empty_integers = try gpa.dupeSentinel(u4, &.{}, 0xf); + gpa.free(empty_integers); + const empty_pointers = try gpa.dupeSentinel(?*anyopaque, &.{}, null); + gpa.free(empty_pointers); } /// Returns a slice with the given new alignment, diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index 72581236e6..9746c8ebc0 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -452,12 +452,17 @@ 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 sentinel: T) Error![:sentinel]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] = sentinel; + return new_buf[0..m.len :sentinel]; +} + +/// 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 { + return dupeSentinel(allocator, T, m, 0); } /// An allocator that always fails to allocate.