authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2025-02-27 16:50:45+01:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2026-03-25 10:50:24+01:00
log3d16c1eb7609fc3793f549e5396fa4da9c2042e9
tree6aef4f9a42e9bc48d6ad90c955a0e16f7e5d6946
parent10256e1b81a52b62581dfe643618aba8dfc64b9e
signaturelock-open Commit is signed but in an unrecognized format.

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

1 files changed, 48 insertions(+), 0 deletions(-)

lib/std/mem.zig+48
......@@ -4766,6 +4766,54 @@ test "sliceAsBytes preserves pointer attributes" {
47664766 try testing.expectEqual(in.alignment, out.alignment);
47674767}
47684768
4769fn AbsorbSentinelReturnType(comptime Slice: type) type {
4770 const info = @typeInfo(Slice).pointer;
4771 assert(info.size == .slice);
4772 return @Pointer(.slice, .{
4773 .@"const" = info.is_const,
4774 .@"volatile" = info.is_volatile,
4775 .@"allowzero" = info.is_allowzero,
4776 .@"addrspace" = info.address_space,
4777 .@"align" = info.alignment,
4778 }, info.child, null);
4779}
4780
4781/// If the provided slice is not sentinel terminated, do nothing and return that slice.
4782/// If it is sentinel-terminated, return a non-sentinel-terminated slice with the
4783/// length increased by one to include the absorbed sentinel element.
4784pub fn absorbSentinel(slice: anytype) AbsorbSentinelReturnType(@TypeOf(slice)) {
4785 const info = @typeInfo(@TypeOf(slice)).pointer;
4786 comptime assert(info.size == .slice);
4787 if (info.sentinel_ptr == null) {
4788 return slice;
4789 } else {
4790 return slice.ptr[0 .. slice.len + 1];
4791 }
4792}
4793
4794test absorbSentinel {
4795 {
4796 var buffer: [3:0]u8 = .{ 1, 2, 3 };
4797 const foo: [:0]const u8 = &buffer;
4798 const bar: []const u8 = &buffer;
4799 try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(foo)));
4800 try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(bar)));
4801 try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 0 }, absorbSentinel(foo));
4802 try testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, absorbSentinel(bar));
4803 }
4804 {
4805 var buffer: [3:0]u8 = .{ 1, 2, 3 };
4806 const foo: [:0]u8 = &buffer;
4807 const bar: []u8 = &buffer;
4808 try testing.expectEqual([]u8, @TypeOf(absorbSentinel(foo)));
4809 try testing.expectEqual([]u8, @TypeOf(absorbSentinel(bar)));
4810 var expected_foo = [_]u8{ 1, 2, 3, 0 };
4811 try testing.expectEqualSlices(u8, &expected_foo, absorbSentinel(foo));
4812 var expected_bar = [_]u8{ 1, 2, 3 };
4813 try testing.expectEqualSlices(u8, &expected_bar, absorbSentinel(bar));
4814 }
4815}
4816
47694817/// Round an address down to the next (or current) aligned address.
47704818/// Unlike `alignForward`, `alignment` can be any positive number, not just a power of 2.
47714819pub fn alignForwardAnyAlign(comptime T: type, addr: T, alignment: T) T {