authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-02 15:55:21+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-02 15:55:21+02:00
log43d84420f3bca06be86397049c3b940358131f75
tree5423fcf89e712c4ec22af18586c48bf833abd336
parent557caecaaa78a1a434b72c38195b40a82fe42c74
parent4848c3a1ad2bfd956419351afb60928e290f2be1

Merge pull request 'std: add mem.absorbSentinel(), fix sentinel handling in Allocator interface' (#31611) from ifreund/zig:absorb-sent into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31611 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

2 files changed, 52 insertions(+), 5 deletions(-)

lib/std/mem.zig+48
...@@ -4709,6 +4709,54 @@ test "sliceAsBytes preserves pointer attributes" {...@@ -4709,6 +4709,54 @@ test "sliceAsBytes preserves pointer attributes" {
4709 try testing.expectEqual(in.alignment, out.alignment);4709 try testing.expectEqual(in.alignment, out.alignment);
4710}4710}
47114711
4712fn AbsorbSentinelReturnType(comptime Slice: type) type {
4713 const info = @typeInfo(Slice).pointer;
4714 assert(info.size == .slice);
4715 return @Pointer(.slice, .{
4716 .@"const" = info.is_const,
4717 .@"volatile" = info.is_volatile,
4718 .@"allowzero" = info.is_allowzero,
4719 .@"addrspace" = info.address_space,
4720 .@"align" = info.alignment,
4721 }, info.child, null);
4722}
4723
4724/// If the provided slice is not sentinel terminated, do nothing and return that slice.
4725/// If it is sentinel-terminated, return a non-sentinel-terminated slice with the
4726/// length increased by one to include the absorbed sentinel element.
4727pub fn absorbSentinel(slice: anytype) AbsorbSentinelReturnType(@TypeOf(slice)) {
4728 const info = @typeInfo(@TypeOf(slice)).pointer;
4729 comptime assert(info.size == .slice);
4730 if (info.sentinel_ptr == null) {
4731 return slice;
4732 } else {
4733 return slice.ptr[0 .. slice.len + 1];
4734 }
4735}
4736
4737test absorbSentinel {
4738 {
4739 var buffer: [3:0]u8 = .{ 1, 2, 3 };
4740 const foo: [:0]const u8 = &buffer;
4741 const bar: []const u8 = &buffer;
4742 try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(foo)));
4743 try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(bar)));
4744 try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 0 }, absorbSentinel(foo));
4745 try testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, absorbSentinel(bar));
4746 }
4747 {
4748 var buffer: [3:0]u8 = .{ 1, 2, 3 };
4749 const foo: [:0]u8 = &buffer;
4750 const bar: []u8 = &buffer;
4751 try testing.expectEqual([]u8, @TypeOf(absorbSentinel(foo)));
4752 try testing.expectEqual([]u8, @TypeOf(absorbSentinel(bar)));
4753 var expected_foo = [_]u8{ 1, 2, 3, 0 };
4754 try testing.expectEqualSlices(u8, &expected_foo, absorbSentinel(foo));
4755 var expected_bar = [_]u8{ 1, 2, 3 };
4756 try testing.expectEqualSlices(u8, &expected_bar, absorbSentinel(bar));
4757 }
4758}
4759
4712/// Round an address down to the next (or current) aligned address.4760/// Round an address down to the next (or current) aligned address.
4713/// Unlike `alignForward`, `alignment` can be any positive number, not just a power of 2.4761/// Unlike `alignForward`, `alignment` can be any positive number, not just a power of 2.
4714pub fn alignForwardAnyAlign(comptime T: type, addr: T, alignment: T) T {4762pub fn alignForwardAnyAlign(comptime T: type, addr: T, alignment: T) T {
lib/std/mem/Allocator.zig+4-5
...@@ -322,7 +322,7 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool {...@@ -322,7 +322,7 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool {
322 if (allocation.len == 0) {322 if (allocation.len == 0) {
323 return false;323 return false;
324 }324 }
325 const old_memory = mem.sliceAsBytes(allocation);325 const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(allocation)));
326 // I would like to use saturating multiplication here, but LLVM cannot lower it326 // I would like to use saturating multiplication here, but LLVM cannot lower it
327 // on WebAssembly: https://github.com/ziglang/zig/issues/9660327 // on WebAssembly: https://github.com/ziglang/zig/issues/9660
328 //const new_len_bytes = new_len *| @sizeOf(T);328 //const new_len_bytes = new_len *| @sizeOf(T);
...@@ -368,7 +368,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allo...@@ -368,7 +368,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allo
368 new_memory.len = new_len;368 new_memory.len = new_len;
369 return new_memory;369 return new_memory;
370 }370 }
371 const old_memory = mem.sliceAsBytes(allocation);371 const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(allocation)));
372 // I would like to use saturating multiplication here, but LLVM cannot lower it372 // I would like to use saturating multiplication here, but LLVM cannot lower it
373 // on WebAssembly: https://github.com/ziglang/zig/issues/9660373 // on WebAssembly: https://github.com/ziglang/zig/issues/9660
374 //const new_len_bytes = new_len *| @sizeOf(T);374 //const new_len_bytes = new_len *| @sizeOf(T);
...@@ -420,7 +420,7 @@ pub fn reallocAdvanced(...@@ -420,7 +420,7 @@ pub fn reallocAdvanced(
420 return ptr;420 return ptr;
421 }421 }
422422
423 const old_byte_slice = mem.sliceAsBytes(old_mem);423 const old_byte_slice: []u8 = @ptrCast(@constCast(mem.absorbSentinel(old_mem)));
424 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return error.OutOfMemory;424 const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return error.OutOfMemory;
425 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure425 // Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
426 if (self.rawRemap(old_byte_slice, .fromByteUnits(slice_info.alignment orelse @alignOf(T)), byte_count, return_address)) |p| {426 if (self.rawRemap(old_byte_slice, .fromByteUnits(slice_info.alignment orelse @alignOf(T)), byte_count, return_address)) |p| {
...@@ -443,8 +443,7 @@ pub fn reallocAdvanced(...@@ -443,8 +443,7 @@ pub fn reallocAdvanced(
443pub fn free(self: Allocator, memory: anytype) void {443pub fn free(self: Allocator, memory: anytype) void {
444 const slice_info = @typeInfo(@TypeOf(memory)).pointer;444 const slice_info = @typeInfo(@TypeOf(memory)).pointer;
445 comptime assert(slice_info.size == .slice);445 comptime assert(slice_info.size == .slice);
446 const mem_with_sent = memory[0 .. memory.len + @intFromBool(slice_info.sentinel() != null)];446 const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory)));
447 const bytes: []u8 = @ptrCast(@constCast(mem_with_sent));
448 if (bytes.len == 0) return;447 if (bytes.len == 0) return;
449 @memset(bytes, undefined);448 @memset(bytes, undefined);
450 self.rawFree(bytes, .fromByteUnits(slice_info.alignment orelse @alignOf(slice_info.child)), @returnAddress());449 self.rawFree(bytes, .fromByteUnits(slice_info.alignment orelse @alignOf(slice_info.child)), @returnAddress());