authorgravatar for lemarkar@flightpath3d.comLeonid Emar-Kar <lemarkar@flightpath3d.com> 2025-02-23 23:38:43+00:00
committergravatar for 46078689+emar-kar@users.noreply.github.comLeo Emar-Kar <46078689+emar-kar@users.noreply.github.com> 2025-05-26 16:54:39+01:00
logc52d9c6c89a30a069cf861adf5abc5528f286a14
tree874a36da4769511e022b38630606d0073f62d5df
parentef35c3d5fefb8c14e17f3c7036bb21e808ee59be

add array list pointer stability


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

lib/std/array_list.zig+91
...@@ -43,6 +43,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -43,6 +43,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
43 capacity: usize,43 capacity: usize,
44 allocator: Allocator,44 allocator: Allocator,
4545
46 /// Used to detect memory safety violations.
47 pointer_stability: debug.SafetyLock = .{},
48
46 pub const Slice = if (alignment) |a| ([]align(a.toByteUnits()) T) else []T;49 pub const Slice = if (alignment) |a| ([]align(a.toByteUnits()) T) else []T;
4750
48 pub fn SentinelSlice(comptime s: T) type {51 pub fn SentinelSlice(comptime s: T) type {
...@@ -69,11 +72,29 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -69,11 +72,29 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
6972
70 /// Release all allocated memory.73 /// Release all allocated memory.
71 pub fn deinit(self: Self) void {74 pub fn deinit(self: Self) void {
75 self.pointer_stability.assertUnlocked();
72 if (@sizeOf(T) > 0) {76 if (@sizeOf(T) > 0) {
73 self.allocator.free(self.allocatedSlice());77 self.allocator.free(self.allocatedSlice());
74 }78 }
75 }79 }
7680
81 /// Puts the array list into a state where any method call that would
82 /// cause an existing value pointer to become invalidated will
83 /// instead trigger an assertion.
84 ///
85 /// An additional call to `lockPointers` in such state also triggers an
86 /// assertion.
87 ///
88 /// `unlockPointers` returns the array list to the previous state.
89 pub fn lockPointers(self: *Self) void {
90 self.pointer_stability.lock();
91 }
92
93 /// Undoes a call to `lockPointers`.
94 pub fn unlockPointers(self: *Self) void {
95 self.pointer_stability.unlock();
96 }
97
77 /// ArrayList takes ownership of the passed in slice. The slice must have been98 /// ArrayList takes ownership of the passed in slice. The slice must have been
78 /// allocated with `gpa`.99 /// allocated with `gpa`.
79 /// Deinitialize with `deinit` or use `toOwnedSlice`.100 /// Deinitialize with `deinit` or use `toOwnedSlice`.
...@@ -99,6 +120,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -99,6 +120,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
99 /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields120 /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
100 /// of this ArrayList. Empties this ArrayList.121 /// of this ArrayList. Empties this ArrayList.
101 pub fn moveToUnmanaged(self: *Self) ArrayListAlignedUnmanaged(T, alignment) {122 pub fn moveToUnmanaged(self: *Self) ArrayListAlignedUnmanaged(T, alignment) {
123 self.pointer_stability.assertUnlocked();
102 const allocator = self.allocator;124 const allocator = self.allocator;
103 const result: ArrayListAlignedUnmanaged(T, alignment) = .{ .items = self.items, .capacity = self.capacity };125 const result: ArrayListAlignedUnmanaged(T, alignment) = .{ .items = self.items, .capacity = self.capacity };
104 self.* = init(allocator);126 self.* = init(allocator);
...@@ -155,6 +177,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -155,6 +177,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
155 /// Asserts that there is enough capacity for the new item.177 /// Asserts that there is enough capacity for the new item.
156 /// Asserts that the index is in bounds or equal to the length.178 /// Asserts that the index is in bounds or equal to the length.
157 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {179 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
180 self.pointer_stability.lock();
181 defer self.pointer_stability.unlock();
182
158 assert(self.items.len < self.capacity);183 assert(self.items.len < self.capacity);
159 self.items.len += 1;184 self.items.len += 1;
160185
...@@ -189,6 +214,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -189,6 +214,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
189 return addManyAtAssumeCapacity(self, index, count);214 return addManyAtAssumeCapacity(self, index, count);
190 }215 }
191216
217 self.pointer_stability.lock();
218 defer self.pointer_stability.unlock();
192 // Make a new allocation, avoiding `ensureTotalCapacity` in order219 // Make a new allocation, avoiding `ensureTotalCapacity` in order
193 // to avoid extra memory copies.220 // to avoid extra memory copies.
194 const new_memory = try self.allocator.alignedAlloc(T, alignment, new_capacity);221 const new_memory = try self.allocator.alignedAlloc(T, alignment, new_capacity);
...@@ -212,6 +239,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -212,6 +239,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
212 /// does not invalidate any before that.239 /// does not invalidate any before that.
213 /// Asserts that the index is in bounds or equal to the length.240 /// Asserts that the index is in bounds or equal to the length.
214 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {241 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
242 self.pointer_stability.lock();
243 defer self.pointer_stability.unlock();
244
215 const new_len = self.items.len + count;245 const new_len = self.items.len + count;
216 assert(self.capacity >= new_len);246 assert(self.capacity >= new_len);
217 const to_move = self.items[index..];247 const to_move = self.items[index..];
...@@ -419,17 +449,25 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -419,17 +449,25 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
419 /// Invalidates element pointers for the elements `items[new_len..]`.449 /// Invalidates element pointers for the elements `items[new_len..]`.
420 /// Asserts that the new length is less than or equal to the previous length.450 /// Asserts that the new length is less than or equal to the previous length.
421 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {451 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
452 self.pointer_stability.lock();
453 defer self.pointer_stability.unlock();
454
422 assert(new_len <= self.items.len);455 assert(new_len <= self.items.len);
423 self.items.len = new_len;456 self.items.len = new_len;
424 }457 }
425458
426 /// Invalidates all element pointers.459 /// Invalidates all element pointers.
427 pub fn clearRetainingCapacity(self: *Self) void {460 pub fn clearRetainingCapacity(self: *Self) void {
461 self.pointer_stability.lock();
462 defer self.pointer_stability.unlock();
463
428 self.items.len = 0;464 self.items.len = 0;
429 }465 }
430466
431 /// Invalidates all element pointers.467 /// Invalidates all element pointers.
432 pub fn clearAndFree(self: *Self) void {468 pub fn clearAndFree(self: *Self) void {
469 self.pointer_stability.assertUnlocked();
470
433 self.allocator.free(self.allocatedSlice());471 self.allocator.free(self.allocatedSlice());
434 self.items.len = 0;472 self.items.len = 0;
435 self.capacity = 0;473 self.capacity = 0;
...@@ -454,6 +492,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -454,6 +492,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
454 /// modify the array so that it can hold exactly `new_capacity` items.492 /// modify the array so that it can hold exactly `new_capacity` items.
455 /// Invalidates element pointers if additional memory is needed.493 /// Invalidates element pointers if additional memory is needed.
456 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {494 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {
495 self.pointer_stability.lock();
496 defer self.pointer_stability.unlock();
497
457 if (@sizeOf(T) == 0) {498 if (@sizeOf(T) == 0) {
458 self.capacity = math.maxInt(usize);499 self.capacity = math.maxInt(usize);
459 return;500 return;
...@@ -559,6 +600,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty...@@ -559,6 +600,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
559 /// Invalidates element pointers to the removed element, if any.600 /// Invalidates element pointers to the removed element, if any.
560 pub fn pop(self: *Self) ?T {601 pub fn pop(self: *Self) ?T {
561 if (self.items.len == 0) return null;602 if (self.items.len == 0) return null;
603
604 self.pointer_stability.lock();
605 defer self.pointer_stability.unlock();
606
562 const val = self.items[self.items.len - 1];607 const val = self.items[self.items.len - 1];
563 self.items.len -= 1;608 self.items.len -= 1;
564 return val;609 return val;
...@@ -631,6 +676,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -631,6 +676,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
631 /// additional memory.676 /// additional memory.
632 capacity: usize = 0,677 capacity: usize = 0,
633678
679 /// Used to detect memory safety violations.
680 pointer_stability: debug.SafetyLock = .{},
681
634 /// An ArrayList containing no elements.682 /// An ArrayList containing no elements.
635 pub const empty: Self = .{683 pub const empty: Self = .{
636 .items = &.{},684 .items = &.{},
...@@ -665,10 +713,28 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -665,10 +713,28 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
665713
666 /// Release all allocated memory.714 /// Release all allocated memory.
667 pub fn deinit(self: *Self, gpa: Allocator) void {715 pub fn deinit(self: *Self, gpa: Allocator) void {
716 self.pointer_stability.assertUnlocked();
668 gpa.free(self.allocatedSlice());717 gpa.free(self.allocatedSlice());
669 self.* = undefined;718 self.* = undefined;
670 }719 }
671720
721 /// Puts the unmanaged array list into a state where any method call that would
722 /// cause an existing value pointer to become invalidated will
723 /// instead trigger an assertion.
724 ///
725 /// An additional call to `lockPointers` in such state also triggers an
726 /// assertion.
727 ///
728 /// `unlockPointers` returns the unmanaged array list to the previous state.
729 pub fn lockPointers(self: *Self) void {
730 self.pointer_stability.lock();
731 }
732
733 /// Undoes a call to `lockPointers`.
734 pub fn unlockPointers(self: *Self) void {
735 self.pointer_stability.unlock();
736 }
737
672 /// Convert this list into an analogous memory-managed one.738 /// Convert this list into an analogous memory-managed one.
673 /// The returned list has ownership of the underlying memory.739 /// The returned list has ownership of the underlying memory.
674 pub fn toManaged(self: *Self, gpa: Allocator) ArrayListAligned(T, alignment) {740 pub fn toManaged(self: *Self, gpa: Allocator) ArrayListAligned(T, alignment) {
...@@ -740,6 +806,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -740,6 +806,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
740 /// Asserts that the list has capacity for one additional item.806 /// Asserts that the list has capacity for one additional item.
741 /// Asserts that the index is in bounds or equal to the length.807 /// Asserts that the index is in bounds or equal to the length.
742 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {808 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
809 self.pointer_stability.lock();
810 defer self.pointer_stability.unlock();
811
743 assert(self.items.len < self.capacity);812 assert(self.items.len < self.capacity);
744 self.items.len += 1;813 self.items.len += 1;
745814
...@@ -775,6 +844,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -775,6 +844,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
775 /// Asserts that the list has capacity for the additional items.844 /// Asserts that the list has capacity for the additional items.
776 /// Asserts that the index is in bounds or equal to the length.845 /// Asserts that the index is in bounds or equal to the length.
777 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {846 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
847 self.pointer_stability.lock();
848 defer self.pointer_stability.unlock();
849
778 const new_len = self.items.len + count;850 const new_len = self.items.len + count;
779 assert(self.capacity >= new_len);851 assert(self.capacity >= new_len);
780 const to_move = self.items[index..];852 const to_move = self.items[index..];
...@@ -843,6 +915,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -843,6 +915,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
843 const dst = self.addManyAtAssumeCapacity(after_range, rest.len);915 const dst = self.addManyAtAssumeCapacity(after_range, rest.len);
844 @memcpy(dst, rest);916 @memcpy(dst, rest);
845 } else {917 } else {
918 self.pointer_stability.lock();
919 defer self.pointer_stability.unlock();
920
846 const extra = range.len - new_items.len;921 const extra = range.len - new_items.len;
847 @memcpy(range[0..new_items.len], new_items);922 @memcpy(range[0..new_items.len], new_items);
848 std.mem.copyForwards(923 std.mem.copyForwards(
...@@ -1044,17 +1119,26 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -1044,17 +1119,26 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
1044 /// Keeps capacity the same.1119 /// Keeps capacity the same.
1045 /// Asserts that the new length is less than or equal to the previous length.1120 /// Asserts that the new length is less than or equal to the previous length.
1046 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {1121 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
1122 self.pointer_stability.lock();
1123 defer self.pointer_stability.unlock();
1124
1047 assert(new_len <= self.items.len);1125 assert(new_len <= self.items.len);
1048 self.items.len = new_len;1126 self.items.len = new_len;
1049 }1127 }
10501128
1051 /// Invalidates all element pointers.1129 /// Invalidates all element pointers.
1052 pub fn clearRetainingCapacity(self: *Self) void {1130 pub fn clearRetainingCapacity(self: *Self) void {
1131 self.pointer_stability.lock();
1132 defer self.pointer_stability.unlock();
1133
1053 self.items.len = 0;1134 self.items.len = 0;
1054 }1135 }
10551136
1056 /// Invalidates all element pointers.1137 /// Invalidates all element pointers.
1057 pub fn clearAndFree(self: *Self, gpa: Allocator) void {1138 pub fn clearAndFree(self: *Self, gpa: Allocator) void {
1139 self.pointer_stability.lock();
1140 defer self.pointer_stability.unlock();
1141
1058 gpa.free(self.allocatedSlice());1142 gpa.free(self.allocatedSlice());
1059 self.items.len = 0;1143 self.items.len = 0;
1060 self.capacity = 0;1144 self.capacity = 0;
...@@ -1072,6 +1156,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -1072,6 +1156,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
1072 /// modify the array so that it can hold exactly `new_capacity` items.1156 /// modify the array so that it can hold exactly `new_capacity` items.
1073 /// Invalidates element pointers if additional memory is needed.1157 /// Invalidates element pointers if additional memory is needed.
1074 pub fn ensureTotalCapacityPrecise(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {1158 pub fn ensureTotalCapacityPrecise(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
1159 self.pointer_stability.lock();
1160 defer self.pointer_stability.unlock();
1161
1075 if (@sizeOf(T) == 0) {1162 if (@sizeOf(T) == 0) {
1076 self.capacity = math.maxInt(usize);1163 self.capacity = math.maxInt(usize);
1077 return;1164 return;
...@@ -1182,6 +1269,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig...@@ -1182,6 +1269,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
1182 /// Invalidates pointers to last element.1269 /// Invalidates pointers to last element.
1183 pub fn pop(self: *Self) ?T {1270 pub fn pop(self: *Self) ?T {
1184 if (self.items.len == 0) return null;1271 if (self.items.len == 0) return null;
1272
1273 self.pointer_stability.lock();
1274 defer self.pointer_stability.unlock();
1275
1185 const val = self.items[self.items.len - 1];1276 const val = self.items[self.items.len - 1];
1186 self.items.len -= 1;1277 self.items.len -= 1;
1187 return val;1278 return val;