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
4343 capacity: usize,
4444 allocator: Allocator,
4545
46 /// Used to detect memory safety violations.
47 pointer_stability: debug.SafetyLock = .{},
48
4649 pub const Slice = if (alignment) |a| ([]align(a.toByteUnits()) T) else []T;
4750
4851 pub fn SentinelSlice(comptime s: T) type {
......@@ -69,11 +72,29 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
6972
7073 /// Release all allocated memory.
7174 pub fn deinit(self: Self) void {
75 self.pointer_stability.assertUnlocked();
7276 if (@sizeOf(T) > 0) {
7377 self.allocator.free(self.allocatedSlice());
7478 }
7579 }
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
7798 /// ArrayList takes ownership of the passed in slice. The slice must have been
7899 /// allocated with `gpa`.
79100 /// Deinitialize with `deinit` or use `toOwnedSlice`.
......@@ -99,6 +120,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
99120 /// Initializes an ArrayListUnmanaged with the `items` and `capacity` fields
100121 /// of this ArrayList. Empties this ArrayList.
101122 pub fn moveToUnmanaged(self: *Self) ArrayListAlignedUnmanaged(T, alignment) {
123 self.pointer_stability.assertUnlocked();
102124 const allocator = self.allocator;
103125 const result: ArrayListAlignedUnmanaged(T, alignment) = .{ .items = self.items, .capacity = self.capacity };
104126 self.* = init(allocator);
......@@ -155,6 +177,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
155177 /// Asserts that there is enough capacity for the new item.
156178 /// Asserts that the index is in bounds or equal to the length.
157179 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
180 self.pointer_stability.lock();
181 defer self.pointer_stability.unlock();
182
158183 assert(self.items.len < self.capacity);
159184 self.items.len += 1;
160185
......@@ -189,6 +214,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
189214 return addManyAtAssumeCapacity(self, index, count);
190215 }
191216
217 self.pointer_stability.lock();
218 defer self.pointer_stability.unlock();
192219 // Make a new allocation, avoiding `ensureTotalCapacity` in order
193220 // to avoid extra memory copies.
194221 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
212239 /// does not invalidate any before that.
213240 /// Asserts that the index is in bounds or equal to the length.
214241 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
242 self.pointer_stability.lock();
243 defer self.pointer_stability.unlock();
244
215245 const new_len = self.items.len + count;
216246 assert(self.capacity >= new_len);
217247 const to_move = self.items[index..];
......@@ -419,17 +449,25 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
419449 /// Invalidates element pointers for the elements `items[new_len..]`.
420450 /// Asserts that the new length is less than or equal to the previous length.
421451 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
452 self.pointer_stability.lock();
453 defer self.pointer_stability.unlock();
454
422455 assert(new_len <= self.items.len);
423456 self.items.len = new_len;
424457 }
425458
426459 /// Invalidates all element pointers.
427460 pub fn clearRetainingCapacity(self: *Self) void {
461 self.pointer_stability.lock();
462 defer self.pointer_stability.unlock();
463
428464 self.items.len = 0;
429465 }
430466
431467 /// Invalidates all element pointers.
432468 pub fn clearAndFree(self: *Self) void {
469 self.pointer_stability.assertUnlocked();
470
433471 self.allocator.free(self.allocatedSlice());
434472 self.items.len = 0;
435473 self.capacity = 0;
......@@ -454,6 +492,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
454492 /// modify the array so that it can hold exactly `new_capacity` items.
455493 /// Invalidates element pointers if additional memory is needed.
456494 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {
495 self.pointer_stability.lock();
496 defer self.pointer_stability.unlock();
497
457498 if (@sizeOf(T) == 0) {
458499 self.capacity = math.maxInt(usize);
459500 return;
......@@ -559,6 +600,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?mem.Alignment) ty
559600 /// Invalidates element pointers to the removed element, if any.
560601 pub fn pop(self: *Self) ?T {
561602 if (self.items.len == 0) return null;
603
604 self.pointer_stability.lock();
605 defer self.pointer_stability.unlock();
606
562607 const val = self.items[self.items.len - 1];
563608 self.items.len -= 1;
564609 return val;
......@@ -631,6 +676,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
631676 /// additional memory.
632677 capacity: usize = 0,
633678
679 /// Used to detect memory safety violations.
680 pointer_stability: debug.SafetyLock = .{},
681
634682 /// An ArrayList containing no elements.
635683 pub const empty: Self = .{
636684 .items = &.{},
......@@ -665,10 +713,28 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
665713
666714 /// Release all allocated memory.
667715 pub fn deinit(self: *Self, gpa: Allocator) void {
716 self.pointer_stability.assertUnlocked();
668717 gpa.free(self.allocatedSlice());
669718 self.* = undefined;
670719 }
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
672738 /// Convert this list into an analogous memory-managed one.
673739 /// The returned list has ownership of the underlying memory.
674740 pub fn toManaged(self: *Self, gpa: Allocator) ArrayListAligned(T, alignment) {
......@@ -740,6 +806,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
740806 /// Asserts that the list has capacity for one additional item.
741807 /// Asserts that the index is in bounds or equal to the length.
742808 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
809 self.pointer_stability.lock();
810 defer self.pointer_stability.unlock();
811
743812 assert(self.items.len < self.capacity);
744813 self.items.len += 1;
745814
......@@ -775,6 +844,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
775844 /// Asserts that the list has capacity for the additional items.
776845 /// Asserts that the index is in bounds or equal to the length.
777846 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
847 self.pointer_stability.lock();
848 defer self.pointer_stability.unlock();
849
778850 const new_len = self.items.len + count;
779851 assert(self.capacity >= new_len);
780852 const to_move = self.items[index..];
......@@ -843,6 +915,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
843915 const dst = self.addManyAtAssumeCapacity(after_range, rest.len);
844916 @memcpy(dst, rest);
845917 } else {
918 self.pointer_stability.lock();
919 defer self.pointer_stability.unlock();
920
846921 const extra = range.len - new_items.len;
847922 @memcpy(range[0..new_items.len], new_items);
848923 std.mem.copyForwards(
......@@ -1044,17 +1119,26 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10441119 /// Keeps capacity the same.
10451120 /// Asserts that the new length is less than or equal to the previous length.
10461121 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
1122 self.pointer_stability.lock();
1123 defer self.pointer_stability.unlock();
1124
10471125 assert(new_len <= self.items.len);
10481126 self.items.len = new_len;
10491127 }
10501128
10511129 /// Invalidates all element pointers.
10521130 pub fn clearRetainingCapacity(self: *Self) void {
1131 self.pointer_stability.lock();
1132 defer self.pointer_stability.unlock();
1133
10531134 self.items.len = 0;
10541135 }
10551136
10561137 /// Invalidates all element pointers.
10571138 pub fn clearAndFree(self: *Self, gpa: Allocator) void {
1139 self.pointer_stability.lock();
1140 defer self.pointer_stability.unlock();
1141
10581142 gpa.free(self.allocatedSlice());
10591143 self.items.len = 0;
10601144 self.capacity = 0;
......@@ -1072,6 +1156,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
10721156 /// modify the array so that it can hold exactly `new_capacity` items.
10731157 /// Invalidates element pointers if additional memory is needed.
10741158 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
10751162 if (@sizeOf(T) == 0) {
10761163 self.capacity = math.maxInt(usize);
10771164 return;
......@@ -1182,6 +1269,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?mem.Alig
11821269 /// Invalidates pointers to last element.
11831270 pub fn pop(self: *Self) ?T {
11841271 if (self.items.len == 0) return null;
1272
1273 self.pointer_stability.lock();
1274 defer self.pointer_stability.unlock();
1275
11851276 const val = self.items[self.items.len - 1];
11861277 self.items.len -= 1;
11871278 return val;