zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Mlib/std/mem.zig | 48++++++++++++++++++++++++++++++++++++++++++++++++
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 {