authorgravatar for rb.lymn@gmail.comRobbie Lyman <rb.lymn@gmail.com> 2026-07-19 18:51:41-04:00
committergravatar for rb.lymn@gmail.comRobbie Lyman <rb.lymn@gmail.com> 2026-07-19 20:46:22-04:00
logccbfd60e32be0412ed12f7d3bfd979dfc5c69e94
treeec0b55bb7ccbc16b722407e19e87ccb8e241f355
parentd5181a9c9bacc1c11930bc3581f9a44c49c27e01
parentc52d9c6c89a30a069cf861adf5abc5528f286a14

feat: add pointer_stability field to ArrayList


1 files changed, 82 insertions(+), 7 deletions(-)

lib/std/array_list.zig+82-7
......@@ -34,6 +34,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
3434 capacity: usize,
3535 allocator: Allocator,
3636
37 /// Used to detect memory safety violations.
38 pointer_stability: debug.SafetyLock = .{},
39
3740 pub const Slice = if (alignment) |a| ([]align(a.toByteUnits()) T) else []T;
3841
3942 pub fn SentinelSlice(comptime s: T) type {
......@@ -60,11 +63,29 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
6063
6164 /// Release all allocated memory.
6265 pub fn deinit(self: Self) void {
66 self.pointer_stability.assertUnlocked();
6367 if (@sizeOf(T) > 0) {
6468 self.allocator.free(self.allocatedSlice());
6569 }
6670 }
6771
72 /// Puts the array list into a state where any method call that would
73 /// cause an existing value pointer to become invalidated will
74 /// instead trigger an assertion.
75 ///
76 /// An additional call to `lockPointers` in such state also triggers an
77 /// assertion.
78 ///
79 /// `unlockPointers` returns the array list to the previous state.
80 pub fn lockPointers(self: *Self) void {
81 self.pointer_stability.lock();
82 }
83
84 /// Undoes a call to `lockPointers`.
85 pub fn unlockPointers(self: *Self) void {
86 self.pointer_stability.unlock();
87 }
88
6889 /// ArrayList takes ownership of the passed in slice. The slice must have been
6990 /// allocated with `gpa`.
7091 /// Deinitialize with `deinit` or use `toOwnedSlice`.
......@@ -90,6 +111,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
90111 /// Initializes an ArrayList with the `items` and `capacity` fields
91112 /// of this ArrayList. Empties this ArrayList.
92113 pub fn moveToUnmanaged(self: *Self) Aligned(T, alignment) {
114 self.pointer_stability.assertUnlocked();
93115 const allocator = self.allocator;
94116 const result: Aligned(T, alignment) = .{ .items = self.items, .capacity = self.capacity };
95117 self.* = init(allocator);
......@@ -146,6 +168,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
146168 /// Asserts that there is enough capacity for the new item.
147169 /// Asserts that the index is in bounds or equal to the length.
148170 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
171 self.pointer_stability.lock();
172 defer self.pointer_stability.unlock();
173
149174 assert(self.items.len < self.capacity);
150175 self.items.len += 1;
151176
......@@ -167,6 +192,8 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
167192 if (self.capacity >= new_len)
168193 return addManyAtAssumeCapacity(self, index, count);
169194
195 self.pointer_stability.lock();
196 defer self.pointer_stability.unlock();
170197 // Here we avoid copying allocated but unused bytes by
171198 // attempting a resize in place, and falling back to allocating
172199 // a new buffer and doing our own copy. With a realloc() call,
......@@ -199,8 +226,6 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
199226 /// elements, which becomes invalid after various `ArrayList`
200227 /// operations.
201228 /// Asserts that there is enough capacity for the new elements.
202 /// Invalidates pre-existing pointers to elements at and after `index`, but
203 /// does not invalidate any before that.
204229 /// Asserts that the index is in bounds or equal to the length.
205230 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
206231 const new_len = self.items.len + count;
......@@ -379,6 +404,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
379404 /// Invalidates element pointers for the elements `items[new_len..]`.
380405 /// Asserts that the new length is less than or equal to the previous length.
381406 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
407 self.pointer_stability.lock();
408 defer self.pointer_stability.unlock();
409
382410 assert(new_len <= self.items.len);
383411 @memset(self.items[new_len..], undefined);
384412 self.items.len = new_len;
......@@ -387,12 +415,15 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
387415 /// Reduce length to 0.
388416 /// Invalidates all element pointers.
389417 pub fn clearRetainingCapacity(self: *Self) void {
418 self.pointer_stability.lock();
419 defer self.pointer_stability.unlock();
390420 @memset(self.items, undefined);
391421 self.items.len = 0;
392422 }
393423
394424 /// Invalidates all element pointers.
395425 pub fn clearAndFree(self: *Self) void {
426 self.pointer_stability.assertUnlocked();
396427 self.allocator.free(self.allocatedSlice());
397428 self.items.len = 0;
398429 self.capacity = 0;
......@@ -418,6 +449,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
418449 /// modify the array so that it can hold exactly `new_capacity` items.
419450 /// Invalidates element pointers if additional memory is needed.
420451 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {
452 self.pointer_stability.lock();
453 defer self.pointer_stability.unlock();
454
421455 if (@sizeOf(T) == 0) {
422456 self.capacity = math.maxInt(usize);
423457 return;
......@@ -523,6 +557,8 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
523557 /// Invalidates element pointers to the removed element, if any.
524558 pub fn pop(self: *Self) ?T {
525559 if (self.items.len == 0) return null;
560 self.pointer_stability.lock();
561 defer self.pointer_stability.unlock();
526562 const val = self.items[self.items.len - 1];
527563 self.items[self.items.len - 1] = undefined;
528564 self.items.len -= 1;
......@@ -584,6 +620,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
584620 /// additional memory.
585621 capacity: usize,
586622
623 /// Used to detect memory safety violations.
624 pointer_stability: debug.SafetyLock = .{},
625
587626 /// An ArrayList containing no elements.
588627 pub const empty: Self = .{
589628 .items = &.{},
......@@ -618,10 +657,28 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
618657
619658 /// Release all allocated memory.
620659 pub fn deinit(self: *Self, gpa: Allocator) void {
660 self.pointer_stability.assertUnlocked();
621661 gpa.free(self.allocatedSlice());
622662 self.* = undefined;
623663 }
624664
665 /// Puts the unmanaged array list into a state where any method call that would
666 /// cause an existing value pointer to become invalidated will
667 /// instead trigger an assertion.
668 ///
669 /// An additional call to `lockPointers` in such state also triggers an
670 /// assertion.
671 ///
672 /// `unlockPointers` returns the unmanaged array list to the previous state.
673 pub fn lockPointers(self: *Self) void {
674 self.pointer_stability.lock();
675 }
676
677 /// Undoes a call to `lockPointers`.
678 pub fn unlockPointers(self: *Self) void {
679 self.pointer_stability.unlock();
680 }
681
625682 /// Convert this list into an analogous memory-managed one.
626683 /// The returned list has ownership of the underlying memory.
627684 pub fn toManaged(self: *Self, gpa: Allocator) AlignedManaged(T, alignment) {
......@@ -718,6 +775,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
718775 ///
719776 /// Asserts that the index is in bounds or equal to the length.
720777 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
778 self.pointer_stability.lock();
779 defer self.pointer_stability.unlock();
780
721781 assert(self.items.len < self.capacity);
722782 self.items.len += 1;
723783
......@@ -763,11 +823,12 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
763823 /// `undefined` values. Returns a slice pointing to the newly allocated
764824 /// elements, which becomes invalid after various `ArrayList`
765825 /// operations.
766 /// Invalidates pre-existing pointers to elements at and after `index`, but
767 /// does not invalidate any before that.
768826 /// Asserts that the list has capacity for the additional items.
769827 /// Asserts that the index is in bounds or equal to the length.
770828 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
829 self.pointer_stability.lock();
830 defer self.pointer_stability.unlock();
831
771832 const new_len = self.items.len + count;
772833 assert(self.capacity >= new_len);
773834 const to_move = self.items[index..];
......@@ -783,9 +844,6 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
783844 /// allocated elements, which becomes invalid after various `ArrayList`
784845 /// operations.
785846 ///
786 /// Invalidates pre-existing pointers to elements at and after `index`, but
787 /// does not invalidate any before that.
788 ///
789847 /// If the list lacks unused capacity for the additional items, returns
790848 /// `error.OutOfMemory`.
791849 ///
......@@ -967,6 +1025,8 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
9671025 /// This operation is O(1).
9681026 /// Asserts that the index is in bounds.
9691027 pub fn swapRemove(self: *Self, i: usize) T {
1028 self.pointer_stability.lock();
1029 defer self.pointer_stability.unlock();
9701030 const val = self.items[i];
9711031 self.items[i] = self.items[self.items.len - 1];
9721032 self.items[self.items.len - 1] = undefined;
......@@ -1128,6 +1188,8 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
11281188 /// Asserts that the new length is less than or equal to the previous length.
11291189 /// If succeds capacity is guaranteed to be equal to the length.
11301190 pub fn shrinkAndFreePrecise(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void {
1191 self.pointer_stability.lock();
1192 defer self.pointer_stability.unlock();
11311193 assert(new_len <= self.items.len);
11321194
11331195 if (@sizeOf(T) == 0) {
......@@ -1181,6 +1243,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
11811243 /// Keeps capacity the same.
11821244 /// Asserts that the new length is less than or equal to the previous length.
11831245 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
1246 self.pointer_stability.lock();
1247 defer self.pointer_stability.unlock();
1248
11841249 assert(new_len <= self.items.len);
11851250 @memset(self.items[new_len..], undefined);
11861251 self.items.len = new_len;
......@@ -1189,12 +1254,16 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
11891254 /// Reduce length to 0.
11901255 /// Invalidates all element pointers.
11911256 pub fn clearRetainingCapacity(self: *Self) void {
1257 self.pointer_stability.lock();
1258 defer self.pointer_stability.unlock();
11921259 @memset(self.items, undefined);
11931260 self.items.len = 0;
11941261 }
11951262
11961263 /// Invalidates all element pointers.
11971264 pub fn clearAndFree(self: *Self, gpa: Allocator) void {
1265 self.pointer_stability.lock();
1266 defer self.pointer_stability.unlock();
11981267 gpa.free(self.allocatedSlice());
11991268 self.items.len = 0;
12001269 self.capacity = 0;
......@@ -1212,6 +1281,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
12121281 /// modify the array so that it can hold exactly `new_capacity` items.
12131282 /// Invalidates element pointers if additional memory is needed.
12141283 pub fn ensureTotalCapacityPrecise(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
1284 self.pointer_stability.lock();
1285 defer self.pointer_stability.unlock();
1286
12151287 if (@sizeOf(T) == 0) {
12161288 self.capacity = math.maxInt(usize);
12171289 return;
......@@ -1371,6 +1443,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
13711443 /// Invalidates pointers to last element.
13721444 pub fn pop(self: *Self) ?T {
13731445 if (self.items.len == 0) return null;
1446 self.pointer_stability.lock();
1447 defer self.pointer_stability.unlock();
1448
13741449 const val = self.items[self.items.len - 1];
13751450 self.items[self.items.len - 1] = undefined;
13761451 self.items.len -= 1;