commit 3d16c1eb7609fc3793f549e5396fa4da9c2042e9 (tree)
parent 10256e1b81a52b62581dfe643618aba8dfc64b9e
Author: Isaac Freund <mail@isaacfreund.com>
Date: Thu, 27 Feb 2025 16:50:45 +0100
std: add mem.absorbSentinel()
This will allow replacing (currently buggy) mem.sliceAsBytes() usage in
the mem.Allocator implementation with, for example:
const bytes: []u8 = @constCast(@ptrCast(mem.absorbSentinel(allocation)));
References: https://github.com/ziglang/zig/pull/22706
Diffstat:
1 file changed, 48 insertions(+), 0 deletions(-)
diff --git a/lib/std/mem.zig b/lib/std/mem.zig
@@ -4766,6 +4766,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 {