| author | |
| committer | |
| log | 276f2e1b810aef048c3ce324e0977ba2d9ff8200 |
| tree | ad66f0e1a007298b564f084e1ffc89d99a191d8c |
| parent | 5152987072ef2d030a704d2b4ca9cebd757d4cec |
| parent | c0924842744fea67df49c9942b99d855b83578e5 |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/36113
Reviewed-by: Andrew Kelley <andrew@ziglang.org>3 files changed, 130 insertions(+), 27 deletions(-)
lib/std/mem.zig+26-12| ... | @@ -9,6 +9,7 @@ const assert = debug.assert; | ... | @@ -9,6 +9,7 @@ const assert = debug.assert; |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const testing = std.testing; | 10 | const testing = std.testing; |
| 11 | const Endian = std.lang.Endian; | 11 | const Endian = std.lang.Endian; |
| 12 | const AbsorbSentinel = std.meta.AbsorbSentinel; | ||
| 12 | 13 | ||
| 13 | /// The standard library currently thoroughly depends on byte size | 14 | /// The standard library currently thoroughly depends on byte size |
| 14 | /// being 8 bits. (see the use of u8 throughout allocation code as | 15 | /// being 8 bits. (see the use of u8 throughout allocation code as |
| ... | @@ -4738,22 +4739,28 @@ test "sliceAsBytes preserves pointer attributes" { | ... | @@ -4738,22 +4739,28 @@ test "sliceAsBytes preserves pointer attributes" { |
| 4738 | try testing.expectEqual(in_attrs.@"align", out_attrs.@"align"); | 4739 | try testing.expectEqual(in_attrs.@"align", out_attrs.@"align"); |
| 4739 | } | 4740 | } |
| 4740 | 4741 | ||
| 4741 | fn AbsorbSentinelReturnType(comptime Slice: type) type { | ||
| 4742 | const info = @typeInfo(Slice).pointer; | ||
| 4743 | assert(info.size == .slice); | ||
| 4744 | return @Pointer(.slice, info.attrs, info.child, null); | ||
| 4745 | } | ||
| 4746 | |||
| 4747 | /// If the provided slice is not sentinel terminated, do nothing and return that slice. | 4742 | /// If the provided slice is not sentinel terminated, do nothing and return that slice. |
| 4748 | /// If it is sentinel-terminated, return a non-sentinel-terminated slice with the | 4743 | /// If it is sentinel-terminated, return a non-sentinel-terminated slice with the |
| 4749 | /// length increased by one to include the absorbed sentinel element. | 4744 | /// length increased by one to include the absorbed sentinel element. |
| 4750 | pub fn absorbSentinel(slice: anytype) AbsorbSentinelReturnType(@TypeOf(slice)) { | 4745 | pub fn absorbSentinel(slice: anytype) AbsorbSentinel(@TypeOf(slice)) { |
| 4751 | const info = @typeInfo(@TypeOf(slice)).pointer; | 4746 | const info = @typeInfo(@TypeOf(slice)).pointer; |
| 4752 | comptime assert(info.size == .slice); | 4747 | switch (info.size) { |
| 4753 | if (info.sentinel_ptr == null) { | 4748 | .slice => { |
| 4754 | return slice; | 4749 | if (info.sentinel_ptr == null) { |
| 4755 | } else { | 4750 | return slice; |
| 4756 | return slice.ptr[0 .. slice.len + 1]; | 4751 | } else { |
| 4752 | return slice.ptr[0 .. slice.len + 1]; | ||
| 4753 | } | ||
| 4754 | }, | ||
| 4755 | .one => { | ||
| 4756 | const child_info = @typeInfo(info.child).array; | ||
| 4757 | if (child_info.sentinel_ptr == null) { | ||
| 4758 | return slice; | ||
| 4759 | } else { | ||
| 4760 | return slice[0 .. child_info.len + 1]; | ||
| 4761 | } | ||
| 4762 | }, | ||
| 4763 | else => unreachable, | ||
| 4757 | } | 4764 | } |
| 4758 | } | 4765 | } |
| 4759 | 4766 | ||
| ... | @@ -4762,21 +4769,28 @@ test absorbSentinel { | ... | @@ -4762,21 +4769,28 @@ test absorbSentinel { |
| 4762 | var buffer: [3:0]u8 = .{ 1, 2, 3 }; | 4769 | var buffer: [3:0]u8 = .{ 1, 2, 3 }; |
| 4763 | const foo: [:0]const u8 = &buffer; | 4770 | const foo: [:0]const u8 = &buffer; |
| 4764 | const bar: []const u8 = &buffer; | 4771 | const bar: []const u8 = &buffer; |
| 4772 | const baz: *const [3:0]u8 = &buffer; | ||
| 4765 | try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(foo))); | 4773 | try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(foo))); |
| 4766 | try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(bar))); | 4774 | try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(bar))); |
| 4775 | try testing.expectEqual(*const [4]u8, @TypeOf(absorbSentinel(baz))); | ||
| 4767 | try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 0 }, absorbSentinel(foo)); | 4776 | try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 0 }, absorbSentinel(foo)); |
| 4768 | try testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, absorbSentinel(bar)); | 4777 | try testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, absorbSentinel(bar)); |
| 4778 | try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 0 }, absorbSentinel(baz)); | ||
| 4769 | } | 4779 | } |
| 4770 | { | 4780 | { |
| 4771 | var buffer: [3:0]u8 = .{ 1, 2, 3 }; | 4781 | var buffer: [3:0]u8 = .{ 1, 2, 3 }; |
| 4772 | const foo: [:0]u8 = &buffer; | 4782 | const foo: [:0]u8 = &buffer; |
| 4773 | const bar: []u8 = &buffer; | 4783 | const bar: []u8 = &buffer; |
| 4784 | const baz: *[3:0]u8 = &buffer; | ||
| 4774 | try testing.expectEqual([]u8, @TypeOf(absorbSentinel(foo))); | 4785 | try testing.expectEqual([]u8, @TypeOf(absorbSentinel(foo))); |
| 4775 | try testing.expectEqual([]u8, @TypeOf(absorbSentinel(bar))); | 4786 | try testing.expectEqual([]u8, @TypeOf(absorbSentinel(bar))); |
| 4787 | try testing.expectEqual(*[4]u8, @TypeOf(absorbSentinel(baz))); | ||
| 4776 | var expected_foo = [_]u8{ 1, 2, 3, 0 }; | 4788 | var expected_foo = [_]u8{ 1, 2, 3, 0 }; |
| 4777 | try testing.expectEqualSlices(u8, &expected_foo, absorbSentinel(foo)); | 4789 | try testing.expectEqualSlices(u8, &expected_foo, absorbSentinel(foo)); |
| 4778 | var expected_bar = [_]u8{ 1, 2, 3 }; | 4790 | var expected_bar = [_]u8{ 1, 2, 3 }; |
| 4779 | try testing.expectEqualSlices(u8, &expected_bar, absorbSentinel(bar)); | 4791 | try testing.expectEqualSlices(u8, &expected_bar, absorbSentinel(bar)); |
| 4792 | var expected_baz = [_]u8{ 1, 2, 3, 0 }; | ||
| 4793 | try testing.expectEqualSlices(u8, &expected_baz, absorbSentinel(baz)); | ||
| 4780 | } | 4794 | } |
| 4781 | } | 4795 | } |
| 4782 | 4796 |
lib/std/mem/Allocator.zig+53-10| ... | @@ -8,6 +8,8 @@ const assert = std.debug.assert; | ... | @@ -8,6 +8,8 @@ const assert = std.debug.assert; |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const mem = std.mem; | 9 | const mem = std.mem; |
| 10 | const Alignment = std.mem.Alignment; | 10 | const Alignment = std.mem.Alignment; |
| 11 | const Slice = std.meta.Slice; | ||
| 12 | const AbsorbSentinel = std.meta.AbsorbSentinel; | ||
| 11 | 13 | ||
| 12 | pub const Error = error{OutOfMemory}; | 14 | pub const Error = error{OutOfMemory}; |
| 13 | pub const Log2Align = math.Log2Int(usize); | 15 | pub const Log2Align = math.Log2Int(usize); |
| ... | @@ -316,8 +318,10 @@ pub fn allocBytesAligned( | ... | @@ -316,8 +318,10 @@ pub fn allocBytesAligned( |
| 316 | /// `new_len` may be zero, in which case the allocation is freed. | 318 | /// `new_len` may be zero, in which case the allocation is freed. |
| 317 | pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { | 319 | pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { |
| 318 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; | 320 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; |
| 319 | comptime assert(slice_info.size == .slice); | 321 | const T = if (slice_info.size != .slice) comptime T: { |
| 320 | const T = slice_info.child; | 322 | assert(slice_info.size == .one); |
| 323 | break :T @typeInfo(slice_info.child).array.child; | ||
| 324 | } else slice_info.child; | ||
| 321 | if (new_len == 0) { | 325 | if (new_len == 0) { |
| 322 | self.free(allocation); | 326 | self.free(allocation); |
| 323 | return true; | 327 | return true; |
| ... | @@ -351,10 +355,12 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { | ... | @@ -351,10 +355,12 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { |
| 351 | /// `new_len` may be zero, in which case the allocation is freed. | 355 | /// `new_len` may be zero, in which case the allocation is freed. |
| 352 | /// | 356 | /// |
| 353 | /// If the allocation's elements' type is zero bytes sized, `allocation.len` is set to `new_len`. | 357 | /// If the allocation's elements' type is zero bytes sized, `allocation.len` is set to `new_len`. |
| 354 | pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allocation) { | 358 | pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?Slice(AbsorbSentinel(@TypeOf(allocation))) { |
| 355 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; | 359 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; |
| 356 | comptime assert(slice_info.size == .slice); | 360 | const T = if (slice_info.size != .slice) comptime T: { |
| 357 | const T = slice_info.child; | 361 | assert(slice_info.size == .one); |
| 362 | break :T @typeInfo(slice_info.child).array.child; | ||
| 363 | } else slice_info.child; | ||
| 358 | 364 | ||
| 359 | if (new_len == 0) { | 365 | if (new_len == 0) { |
| 360 | self.free(allocation); | 366 | self.free(allocation); |
| ... | @@ -393,7 +399,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allo | ... | @@ -393,7 +399,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allo |
| 393 | /// do the realloc more efficiently than the caller | 399 | /// do the realloc more efficiently than the caller |
| 394 | /// * `resize` which returns `false` when the `Allocator` implementation cannot | 400 | /// * `resize` which returns `false` when the `Allocator` implementation cannot |
| 395 | /// change the size without relocating the allocation. | 401 | /// change the size without relocating the allocation. |
| 396 | pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!@TypeOf(old_mem) { | 402 | pub fn realloc(self: Allocator, old_mem: anytype, new_n: usize) Error!Slice(AbsorbSentinel(@TypeOf(old_mem))) { |
| 397 | return self.reallocAdvanced(old_mem, new_n, @returnAddress()); | 403 | return self.reallocAdvanced(old_mem, new_n, @returnAddress()); |
| 398 | } | 404 | } |
| 399 | 405 | ||
| ... | @@ -402,10 +408,12 @@ pub fn reallocAdvanced( | ... | @@ -402,10 +408,12 @@ pub fn reallocAdvanced( |
| 402 | old_mem: anytype, | 408 | old_mem: anytype, |
| 403 | new_n: usize, | 409 | new_n: usize, |
| 404 | return_address: usize, | 410 | return_address: usize, |
| 405 | ) Error!@TypeOf(old_mem) { | 411 | ) Error!Slice(AbsorbSentinel(@TypeOf(old_mem))) { |
| 406 | const slice_info = @typeInfo(@TypeOf(old_mem)).pointer; | 412 | const slice_info = @typeInfo(@TypeOf(old_mem)).pointer; |
| 407 | comptime assert(slice_info.size == .slice); | 413 | const T = if (slice_info.size != .slice) comptime T: { |
| 408 | const T = slice_info.child; | 414 | assert(slice_info.size == .one); |
| 415 | break :T @typeInfo(slice_info.child).array.child; | ||
| 416 | } else slice_info.child; | ||
| 409 | if (old_mem.len == 0) { | 417 | if (old_mem.len == 0) { |
| 410 | return self.allocAdvancedWithRetAddr(T, .fromByteUnitsOptional(slice_info.attrs.@"align"), new_n, return_address); | 418 | return self.allocAdvancedWithRetAddr(T, .fromByteUnitsOptional(slice_info.attrs.@"align"), new_n, return_address); |
| 411 | } | 419 | } |
| ... | @@ -440,7 +448,6 @@ pub fn reallocAdvanced( | ... | @@ -440,7 +448,6 @@ pub fn reallocAdvanced( |
| 440 | pub fn free(self: Allocator, memory: anytype) void { | 448 | pub fn free(self: Allocator, memory: anytype) void { |
| 441 | const slice_info = @typeInfo(@TypeOf(memory)).pointer; | 449 | const slice_info = @typeInfo(@TypeOf(memory)).pointer; |
| 442 | if (slice_info.size != .slice) { | 450 | if (slice_info.size != .slice) { |
| 443 | // slicing with comptime-known start and end results in *[len]T, which may be free'd | ||
| 444 | comptime assert(slice_info.size == .one and @typeInfo(slice_info.child) == .array); | 451 | comptime assert(slice_info.size == .one and @typeInfo(slice_info.child) == .array); |
| 445 | } | 452 | } |
| 446 | const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory))); | 453 | const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory))); |
| ... | @@ -585,3 +592,39 @@ test failing { | ... | @@ -585,3 +592,39 @@ test failing { |
| 585 | try std.testing.expectError(error.OutOfMemory, f.alloc(u8, std.math.maxInt(usize))); | 592 | try std.testing.expectError(error.OutOfMemory, f.alloc(u8, std.math.maxInt(usize))); |
| 586 | try std.testing.expectError(error.OutOfMemory, f.allocSentinel(u8, std.math.maxInt(usize) - 1, 0)); | 593 | try std.testing.expectError(error.OutOfMemory, f.allocSentinel(u8, std.math.maxInt(usize) - 1, 0)); |
| 587 | } | 594 | } |
| 595 | |||
| 596 | test "free single-pointer to array" { | ||
| 597 | const allocator = std.testing.allocator; | ||
| 598 | { | ||
| 599 | const allocation = try allocator.alloc(u32, 128); | ||
| 600 | allocation[127] = 0; | ||
| 601 | const ptr: *[127:0]u32 = allocation[0..127 :0]; | ||
| 602 | allocator.free(ptr); | ||
| 603 | } | ||
| 604 | { | ||
| 605 | const allocation = try allocator.alloc(u32, 128); | ||
| 606 | allocation[127] = 0; | ||
| 607 | const ptr: *[127:0]u32 = allocation[0..127 :0]; | ||
| 608 | if (allocator.resize(ptr, 16)) { | ||
| 609 | allocator.free(ptr[0..16]); | ||
| 610 | } else allocator.free(ptr); | ||
| 611 | } | ||
| 612 | { | ||
| 613 | const allocation = try allocator.alloc(u32, 128); | ||
| 614 | allocation[127] = 0; | ||
| 615 | const ptr: *[127:0]u32 = allocation[0..127 :0]; | ||
| 616 | if (allocator.remap(ptr, 16)) |new| { | ||
| 617 | allocator.free(new); | ||
| 618 | } else allocator.free(ptr); | ||
| 619 | } | ||
| 620 | { | ||
| 621 | const allocation = try allocator.alloc(u32, 128); | ||
| 622 | allocation[127] = 0; | ||
| 623 | const ptr: *[127:0]u32 = allocation[0..127 :0]; | ||
| 624 | if (allocator.realloc(ptr, 16)) |new| { | ||
| 625 | allocator.free(new); | ||
| 626 | } else |_| { | ||
| 627 | allocator.free(allocation); | ||
| 628 | } | ||
| 629 | } | ||
| 630 | } |
lib/std/meta.zig+51-5| ... | @@ -1,10 +1,9 @@ | ... | @@ -1,10 +1,9 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | |||
| 2 | const std = @import("std.zig"); | 3 | const std = @import("std.zig"); |
| 3 | const debug = std.debug; | 4 | const assert = std.debug.assert; |
| 4 | const mem = std.mem; | 5 | const mem = std.mem; |
| 5 | const math = std.math; | ||
| 6 | const testing = std.testing; | 6 | const testing = std.testing; |
| 7 | const root = @import("root"); | ||
| 8 | 7 | ||
| 9 | pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; | 8 | pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; |
| 10 | 9 | ||
| ... | @@ -821,8 +820,8 @@ pub fn isError(error_union: anytype) bool { | ... | @@ -821,8 +820,8 @@ pub fn isError(error_union: anytype) bool { |
| 821 | } | 820 | } |
| 822 | 821 | ||
| 823 | test isError { | 822 | test isError { |
| 824 | try std.testing.expect(isError(math.divTrunc(u8, 5, 0))); | 823 | try std.testing.expect(isError(std.math.divTrunc(u8, 5, 0))); |
| 825 | try std.testing.expect(!isError(math.divTrunc(u8, 5, 5))); | 824 | try std.testing.expect(!isError(std.math.divTrunc(u8, 5, 5))); |
| 826 | } | 825 | } |
| 827 | 826 | ||
| 828 | /// Returns true if a type has a namespace and the namespace contains `name`; | 827 | /// Returns true if a type has a namespace and the namespace contains `name`; |
| ... | @@ -1070,3 +1069,50 @@ test hasUniqueRepresentation { | ... | @@ -1070,3 +1069,50 @@ test hasUniqueRepresentation { |
| 1070 | 1069 | ||
| 1071 | try testing.expect(hasUniqueRepresentation(StructWithComptimeFields)); | 1070 | try testing.expect(hasUniqueRepresentation(StructWithComptimeFields)); |
| 1072 | } | 1071 | } |
| 1072 | |||
| 1073 | /// Given a pointer type, type-erases the array length if present, returning an | ||
| 1074 | /// equivalent pointer type that is always a slice. | ||
| 1075 | pub fn Slice(comptime Pointer: type) type { | ||
| 1076 | const info = @typeInfo(Pointer).pointer; | ||
| 1077 | switch (info.size) { | ||
| 1078 | .slice => return Pointer, | ||
| 1079 | .one => { | ||
| 1080 | const child_info = @typeInfo(info.child); | ||
| 1081 | comptime assert(child_info == .array); | ||
| 1082 | const sentinel_ptr: ?*const child_info.array.child = @ptrCast(@alignCast(child_info.array.sentinel_ptr)); | ||
| 1083 | return @Pointer( | ||
| 1084 | .slice, | ||
| 1085 | info.attrs, | ||
| 1086 | child_info.array.child, | ||
| 1087 | if (sentinel_ptr) |ptr| ptr.* else null, | ||
| 1088 | ); | ||
| 1089 | }, | ||
| 1090 | else => unreachable, | ||
| 1091 | } | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | /// Given a pointer type, removes the sentinel if present, returning an | ||
| 1095 | /// equivalent pointer type with no sentinel | ||
| 1096 | pub fn AbsorbSentinel(comptime Pointer: type) type { | ||
| 1097 | const info = @typeInfo(Pointer).pointer; | ||
| 1098 | switch (info.size) { | ||
| 1099 | .slice => return @Pointer(.slice, info.attrs, info.child, null), | ||
| 1100 | .one => { | ||
| 1101 | const child_info = @typeInfo(info.child).array; | ||
| 1102 | if (child_info.sentinel_ptr == null) { | ||
| 1103 | return Pointer; | ||
| 1104 | } else { | ||
| 1105 | return @Pointer(.one, info.attrs, [child_info.len + 1]child_info.child, null); | ||
| 1106 | } | ||
| 1107 | }, | ||
| 1108 | else => unreachable, | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | |||
| 1112 | test Slice { | ||
| 1113 | try testing.expectEqual([]i32, Slice(*[10]i32)); | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | test AbsorbSentinel { | ||
| 1117 | try testing.expectEqual(*[5]u32, AbsorbSentinel(*[4:0]u32)); | ||
| 1118 | } |