diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 311b5701a0e3..3b05a11f6b51 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -4418,6 +4418,59 @@ 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 @Type(.{ + .pointer = .{ + .size = info.size, + .is_const = info.is_const, + .is_volatile = info.is_volatile, + .is_allowzero = info.is_allowzero, + .alignment = info.alignment, + .address_space = info.address_space, + .child = info.child, + .sentinel_ptr = 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; + 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 { diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index dc3e3f0325e4..44b02143a5ff 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -304,7 +304,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 = @constCast(@ptrCast(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); @@ -348,7 +348,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) t: { new_memory.len = new_len; return new_memory; } - const old_memory = mem.sliceAsBytes(allocation); + const old_memory: []u8 = @constCast(@ptrCast(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); @@ -397,7 +397,7 @@ pub fn reallocAdvanced( return @as([*]align(Slice.alignment) T, @ptrFromInt(ptr))[0..0]; } - const old_byte_slice = mem.sliceAsBytes(old_mem); + const old_byte_slice: []u8 = @constCast(@ptrCast(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.alignment), byte_count, return_address)) |p| { @@ -421,12 +421,10 @@ pub fn reallocAdvanced( /// To free a single item, see `destroy`. pub fn free(self: Allocator, memory: anytype) void { const Slice = @typeInfo(@TypeOf(memory)).pointer; - const bytes = mem.sliceAsBytes(memory); - const bytes_len = bytes.len + if (Slice.sentinel() != null) @sizeOf(Slice.child) else 0; - if (bytes_len == 0) return; - const non_const_ptr = @constCast(bytes.ptr); - @memset(non_const_ptr[0..bytes_len], undefined); - self.rawFree(non_const_ptr[0..bytes_len], .fromByteUnits(Slice.alignment), @returnAddress()); + const bytes: []u8 = @constCast(@ptrCast(mem.absorbSentinel(memory))); + if (bytes.len == 0) return; + @memset(bytes, undefined); + self.rawFree(bytes, .fromByteUnits(Slice.alignment), @returnAddress()); } /// Copies `m` to newly allocated memory. Caller owns the memory.