| 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 | 9 | const math = std.math; |
| 10 | 10 | const testing = std.testing; |
| 11 | 11 | const Endian = std.lang.Endian; |
| 12 | const AbsorbSentinel = std.meta.AbsorbSentinel; | |
| 12 | 13 | |
| 13 | 14 | /// The standard library currently thoroughly depends on byte size |
| 14 | 15 | /// being 8 bits. (see the use of u8 throughout allocation code as |
| ... | ... | @@ -4738,22 +4739,28 @@ test "sliceAsBytes preserves pointer attributes" { |
| 4738 | 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 | 4742 | /// If the provided slice is not sentinel terminated, do nothing and return that slice. |
| 4748 | 4743 | /// If it is sentinel-terminated, return a non-sentinel-terminated slice with the |
| 4749 | 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 | 4746 | const info = @typeInfo(@TypeOf(slice)).pointer; |
| 4752 | comptime assert(info.size == .slice); | |
| 4753 | if (info.sentinel_ptr == null) { | |
| 4754 | return slice; | |
| 4755 | } else { | |
| 4756 | return slice.ptr[0 .. slice.len + 1]; | |
| 4747 | switch (info.size) { | |
| 4748 | .slice => { | |
| 4749 | if (info.sentinel_ptr == null) { | |
| 4750 | return slice; | |
| 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 | 4769 | var buffer: [3:0]u8 = .{ 1, 2, 3 }; |
| 4763 | 4770 | const foo: [:0]const u8 = &buffer; |
| 4764 | 4771 | const bar: []const u8 = &buffer; |
| 4772 | const baz: *const [3:0]u8 = &buffer; | |
| 4765 | 4773 | try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(foo))); |
| 4766 | 4774 | try testing.expectEqual([]const u8, @TypeOf(absorbSentinel(bar))); |
| 4775 | try testing.expectEqual(*const [4]u8, @TypeOf(absorbSentinel(baz))); | |
| 4767 | 4776 | try testing.expectEqualSlices(u8, &.{ 1, 2, 3, 0 }, absorbSentinel(foo)); |
| 4768 | 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 | 4781 | var buffer: [3:0]u8 = .{ 1, 2, 3 }; |
| 4772 | 4782 | const foo: [:0]u8 = &buffer; |
| 4773 | 4783 | const bar: []u8 = &buffer; |
| 4784 | const baz: *[3:0]u8 = &buffer; | |
| 4774 | 4785 | try testing.expectEqual([]u8, @TypeOf(absorbSentinel(foo))); |
| 4775 | 4786 | try testing.expectEqual([]u8, @TypeOf(absorbSentinel(bar))); |
| 4787 | try testing.expectEqual(*[4]u8, @TypeOf(absorbSentinel(baz))); | |
| 4776 | 4788 | var expected_foo = [_]u8{ 1, 2, 3, 0 }; |
| 4777 | 4789 | try testing.expectEqualSlices(u8, &expected_foo, absorbSentinel(foo)); |
| 4778 | 4790 | var expected_bar = [_]u8{ 1, 2, 3 }; |
| 4779 | 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 | 8 | const math = std.math; |
| 9 | 9 | const mem = std.mem; |
| 10 | 10 | const Alignment = std.mem.Alignment; |
| 11 | const Slice = std.meta.Slice; | |
| 12 | const AbsorbSentinel = std.meta.AbsorbSentinel; | |
| 11 | 13 | |
| 12 | 14 | pub const Error = error{OutOfMemory}; |
| 13 | 15 | pub const Log2Align = math.Log2Int(usize); |
| ... | ... | @@ -316,8 +318,10 @@ pub fn allocBytesAligned( |
| 316 | 318 | /// `new_len` may be zero, in which case the allocation is freed. |
| 317 | 319 | pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { |
| 318 | 320 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; |
| 319 | comptime assert(slice_info.size == .slice); | |
| 320 | const T = slice_info.child; | |
| 321 | const T = if (slice_info.size != .slice) comptime T: { | |
| 322 | assert(slice_info.size == .one); | |
| 323 | break :T @typeInfo(slice_info.child).array.child; | |
| 324 | } else slice_info.child; | |
| 321 | 325 | if (new_len == 0) { |
| 322 | 326 | self.free(allocation); |
| 323 | 327 | return true; |
| ... | ... | @@ -351,10 +355,12 @@ pub fn resize(self: Allocator, allocation: anytype, new_len: usize) bool { |
| 351 | 355 | /// `new_len` may be zero, in which case the allocation is freed. |
| 352 | 356 | /// |
| 353 | 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 | 359 | const slice_info = @typeInfo(@TypeOf(allocation)).pointer; |
| 356 | comptime assert(slice_info.size == .slice); | |
| 357 | const T = slice_info.child; | |
| 360 | const T = if (slice_info.size != .slice) comptime T: { | |
| 361 | assert(slice_info.size == .one); | |
| 362 | break :T @typeInfo(slice_info.child).array.child; | |
| 363 | } else slice_info.child; | |
| 358 | 364 | |
| 359 | 365 | if (new_len == 0) { |
| 360 | 366 | self.free(allocation); |
| ... | ... | @@ -393,7 +399,7 @@ pub fn remap(self: Allocator, allocation: anytype, new_len: usize) ?@TypeOf(allo |
| 393 | 399 | /// do the realloc more efficiently than the caller |
| 394 | 400 | /// * `resize` which returns `false` when the `Allocator` implementation cannot |
| 395 | 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 | 403 | return self.reallocAdvanced(old_mem, new_n, @returnAddress()); |
| 398 | 404 | } |
| 399 | 405 | |
| ... | ... | @@ -402,10 +408,12 @@ pub fn reallocAdvanced( |
| 402 | 408 | old_mem: anytype, |
| 403 | 409 | new_n: usize, |
| 404 | 410 | return_address: usize, |
| 405 | ) Error!@TypeOf(old_mem) { | |
| 411 | ) Error!Slice(AbsorbSentinel(@TypeOf(old_mem))) { | |
| 406 | 412 | const slice_info = @typeInfo(@TypeOf(old_mem)).pointer; |
| 407 | comptime assert(slice_info.size == .slice); | |
| 408 | const T = slice_info.child; | |
| 413 | const T = if (slice_info.size != .slice) comptime T: { | |
| 414 | assert(slice_info.size == .one); | |
| 415 | break :T @typeInfo(slice_info.child).array.child; | |
| 416 | } else slice_info.child; | |
| 409 | 417 | if (old_mem.len == 0) { |
| 410 | 418 | return self.allocAdvancedWithRetAddr(T, .fromByteUnitsOptional(slice_info.attrs.@"align"), new_n, return_address); |
| 411 | 419 | } |
| ... | ... | @@ -440,7 +448,6 @@ pub fn reallocAdvanced( |
| 440 | 448 | pub fn free(self: Allocator, memory: anytype) void { |
| 441 | 449 | const slice_info = @typeInfo(@TypeOf(memory)).pointer; |
| 442 | 450 | if (slice_info.size != .slice) { |
| 443 | // slicing with comptime-known start and end results in *[len]T, which may be free'd | |
| 444 | 451 | comptime assert(slice_info.size == .one and @typeInfo(slice_info.child) == .array); |
| 445 | 452 | } |
| 446 | 453 | const bytes: []u8 = @ptrCast(@constCast(mem.absorbSentinel(memory))); |
| ... | ... | @@ -585,3 +592,39 @@ test failing { |
| 585 | 592 | try std.testing.expectError(error.OutOfMemory, f.alloc(u8, std.math.maxInt(usize))); |
| 586 | 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 | 1 | const builtin = @import("builtin"); |
| 2 | ||
| 2 | 3 | const std = @import("std.zig"); |
| 3 | const debug = std.debug; | |
| 4 | const assert = std.debug.assert; | |
| 4 | 5 | const mem = std.mem; |
| 5 | const math = std.math; | |
| 6 | 6 | const testing = std.testing; |
| 7 | const root = @import("root"); | |
| 8 | 7 | |
| 9 | 8 | pub const TrailerFlags = @import("meta/trailer_flags.zig").TrailerFlags; |
| 10 | 9 | |
| ... | ... | @@ -821,8 +820,8 @@ pub fn isError(error_union: anytype) bool { |
| 821 | 820 | } |
| 822 | 821 | |
| 823 | 822 | test isError { |
| 824 | try std.testing.expect(isError(math.divTrunc(u8, 5, 0))); | |
| 825 | try std.testing.expect(!isError(math.divTrunc(u8, 5, 5))); | |
| 823 | try std.testing.expect(isError(std.math.divTrunc(u8, 5, 0))); | |
| 824 | try std.testing.expect(!isError(std.math.divTrunc(u8, 5, 5))); | |
| 826 | 825 | } |
| 827 | 826 | |
| 828 | 827 | /// Returns true if a type has a namespace and the namespace contains `name`; |
| ... | ... | @@ -1070,3 +1069,50 @@ test hasUniqueRepresentation { |
| 1070 | 1069 | |
| 1071 | 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 | } |