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>
This commit is contained in:
Andrew Kelley
2026-04-02 15:55:21 +02:00
2 changed files with 52 additions and 5 deletions
+48
View File
@@ -4709,6 +4709,54 @@ test "sliceAsBytes preserves pointer attributes" {
try testing.expectEqual(in.alignment, out.alignment);
}
fn AbsorbSentinelReturnType(comptime Slice: type) type {
const info = @typeInfo(Slice).pointer;
assert(info.size == .slice);
return @Pointer(.slice, .{
.@"const" = info.is_const,
.@"volatile" = info.is_volatile,
.@"allowzero" = info.is_allowzero,
.@"addrspace" = info.address_space,
.@"align" = info.alignment,
}, info.child, null);
}
/// If the provided slice is not sentinel terminated, do nothing and return that slice.
/// If it is sentinel-terminated, return a non-sentinel-terminated slice with the
/// length increased by one to include the absorbed sentinel element.
pub fn absorbSentinel(slice: anytype) AbsorbSentinelReturnType(@TypeOf(slice)) {
const info = @typeInfo(@TypeOf(slice)).pointer;
comptime assert(info.size == .slice);
if (info.sentinel_ptr == null) {
return slice;
} else {
return slice.ptr[0 .. slice.len + 1];
}
}
test absorbSentinel {
{
var buffer: [3:0]u8 = .{ 1, 2, 3 };
const foo: [:0]const u8 = &buffer;
const bar: []const u8 = &buffer;
try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(foo)));
try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(bar)));
try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 0 }, absorbSentinel(foo));
try testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, absorbSentinel(bar));
}
{
var buffer: [3:0]u8 = .{ 1, 2, 3 };
const foo: [:0]u8 = &buffer;
const bar: []u8 = &buffer;
try testing.expectEqual([]u8, @TypeOf(absorbSentinel(foo)));
try testing.expectEqual([]u8, @TypeOf(absorbSentinel(bar)));
var expected_foo = [_]u8{ 1, 2, 3, 0 };
try testing.expectEqualSlices(u8, &expected_foo, absorbSentinel(foo));
var expected_bar = [_]u8{ 1, 2, 3 };
try testing.expectEqualSlices(u8, &expected_bar, absorbSentinel(bar));
}
}
/// Round an address down to the next (or current) aligned address.
/// Unlike `alignForward`, `alignment` can be any positive number, not just a power of 2.
pub fn alignForwardAnyAlign(comptime T: type, addr: T, alignment: T) T {
+4 -5
View File
@@ -322,7 +322,7 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool {
if (allocation.len == 0) {
return false;
}
const old_memory = mem.sliceAsBytes(allocation);
const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(allocation)));
// I would like to use saturating multiplication here, but LLVM cannot lower it
// on WebAssembly: https://github.com/ziglang/zig/issues/9660
//const new_len_bytes = new_len *| @sizeOf(T);
@@ -368,7 +368,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allo
new_memory.len = new_len;
return new_memory;
}
const old_memory = mem.sliceAsBytes(allocation);
const old_memory: []u8 = @ptrCast(@constCast(mem.absorbSentinel(allocation)));
// I would like to use saturating multiplication here, but LLVM cannot lower it
// on WebAssembly: https://github.com/ziglang/zig/issues/9660
//const new_len_bytes = new_len *| @sizeOf(T);
@@ -420,7 +420,7 @@ pub fn reallocAdvanced(
return ptr;
}
const old_byte_slice = mem.sliceAsBytes(old_mem);
const old_byte_slice: []u8 = @ptrCast(@constCast(mem.absorbSentinel(old_mem)));
const byte_count = math.mul(usize, @sizeOf(T), new_n) catch return error.OutOfMemory;
// Note: can't set shrunk memory to undefined as memory shouldn't be modified on realloc failure
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(
pub fn free(self: Allocator, memory: anytype) void {
const slice_info = @typeInfo(@TypeOf(memory)).pointer;
comptime assert(slice_info.size == .slice);
const mem_with_sent = memory[0 .. memory.len + @intFromBool(slice_info.sentinel() != null)];
const bytes: []u8 = @ptrCast(@constCast(mem_with_sent));
const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory)));
if (bytes.len == 0) return;
@memset(bytes, undefined);
self.rawFree(bytes, .fromByteUnits(slice_info.alignment orelse @alignOf(slice_info.child)), @returnAddress());