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...@@ -34,6 +34,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
34 capacity: usize,34 capacity: usize,
35 allocator: Allocator,35 allocator: Allocator,
3636
37 /// Used to detect memory safety violations.
38 pointer_stability: debug.SafetyLock = .{},
39
37 pub const Slice = if (alignment) |a| ([]align(a.toByteUnits()) T) else []T;40 pub const Slice = if (alignment) |a| ([]align(a.toByteUnits()) T) else []T;
3841
39 pub fn SentinelSlice(comptime s: T) type {42 pub fn SentinelSlice(comptime s: T) type {
...@@ -60,11 +63,29 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -60,11 +63,29 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
6063
61 /// Release all allocated memory.64 /// Release all allocated memory.
62 pub fn deinit(self: Self) void {65 pub fn deinit(self: Self) void {
66 self.pointer_stability.assertUnlocked();
63 if (@sizeOf(T) > 0) {67 if (@sizeOf(T) > 0) {
64 self.allocator.free(self.allocatedSlice());68 self.allocator.free(self.allocatedSlice());
65 }69 }
66 }70 }
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
68 /// ArrayList takes ownership of the passed in slice. The slice must have been89 /// ArrayList takes ownership of the passed in slice. The slice must have been
69 /// allocated with `gpa`.90 /// allocated with `gpa`.
70 /// Deinitialize with `deinit` or use `toOwnedSlice`.91 /// Deinitialize with `deinit` or use `toOwnedSlice`.
...@@ -90,6 +111,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -90,6 +111,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
90 /// Initializes an ArrayList with the `items` and `capacity` fields111 /// Initializes an ArrayList with the `items` and `capacity` fields
91 /// of this ArrayList. Empties this ArrayList.112 /// of this ArrayList. Empties this ArrayList.
92 pub fn moveToUnmanaged(self: *Self) Aligned(T, alignment) {113 pub fn moveToUnmanaged(self: *Self) Aligned(T, alignment) {
114 self.pointer_stability.assertUnlocked();
93 const allocator = self.allocator;115 const allocator = self.allocator;
94 const result: Aligned(T, alignment) = .{ .items = self.items, .capacity = self.capacity };116 const result: Aligned(T, alignment) = .{ .items = self.items, .capacity = self.capacity };
95 self.* = init(allocator);117 self.* = init(allocator);
...@@ -146,6 +168,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -146,6 +168,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
146 /// Asserts that there is enough capacity for the new item.168 /// Asserts that there is enough capacity for the new item.
147 /// Asserts that the index is in bounds or equal to the length.169 /// Asserts that the index is in bounds or equal to the length.
148 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {170 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
171 self.pointer_stability.lock();
172 defer self.pointer_stability.unlock();
173
149 assert(self.items.len < self.capacity);174 assert(self.items.len < self.capacity);
150 self.items.len += 1;175 self.items.len += 1;
151176
...@@ -167,6 +192,8 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -167,6 +192,8 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
167 if (self.capacity >= new_len)192 if (self.capacity >= new_len)
168 return addManyAtAssumeCapacity(self, index, count);193 return addManyAtAssumeCapacity(self, index, count);
169194
195 self.pointer_stability.lock();
196 defer self.pointer_stability.unlock();
170 // Here we avoid copying allocated but unused bytes by197 // Here we avoid copying allocated but unused bytes by
171 // attempting a resize in place, and falling back to allocating198 // attempting a resize in place, and falling back to allocating
172 // a new buffer and doing our own copy. With a realloc() call,199 // 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...@@ -199,8 +226,6 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
199 /// elements, which becomes invalid after various `ArrayList`226 /// elements, which becomes invalid after various `ArrayList`
200 /// operations.227 /// operations.
201 /// Asserts that there is enough capacity for the new elements.228 /// 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.
204 /// Asserts that the index is in bounds or equal to the length.229 /// Asserts that the index is in bounds or equal to the length.
205 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {230 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
206 const new_len = self.items.len + count;231 const new_len = self.items.len + count;
...@@ -379,6 +404,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -379,6 +404,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
379 /// Invalidates element pointers for the elements `items[new_len..]`.404 /// Invalidates element pointers for the elements `items[new_len..]`.
380 /// Asserts that the new length is less than or equal to the previous length.405 /// Asserts that the new length is less than or equal to the previous length.
381 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {406 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
407 self.pointer_stability.lock();
408 defer self.pointer_stability.unlock();
409
382 assert(new_len <= self.items.len);410 assert(new_len <= self.items.len);
383 @memset(self.items[new_len..], undefined);411 @memset(self.items[new_len..], undefined);
384 self.items.len = new_len;412 self.items.len = new_len;
...@@ -387,12 +415,15 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -387,12 +415,15 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
387 /// Reduce length to 0.415 /// Reduce length to 0.
388 /// Invalidates all element pointers.416 /// Invalidates all element pointers.
389 pub fn clearRetainingCapacity(self: *Self) void {417 pub fn clearRetainingCapacity(self: *Self) void {
418 self.pointer_stability.lock();
419 defer self.pointer_stability.unlock();
390 @memset(self.items, undefined);420 @memset(self.items, undefined);
391 self.items.len = 0;421 self.items.len = 0;
392 }422 }
393423
394 /// Invalidates all element pointers.424 /// Invalidates all element pointers.
395 pub fn clearAndFree(self: *Self) void {425 pub fn clearAndFree(self: *Self) void {
426 self.pointer_stability.assertUnlocked();
396 self.allocator.free(self.allocatedSlice());427 self.allocator.free(self.allocatedSlice());
397 self.items.len = 0;428 self.items.len = 0;
398 self.capacity = 0;429 self.capacity = 0;
...@@ -418,6 +449,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -418,6 +449,9 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
418 /// modify the array so that it can hold exactly `new_capacity` items.449 /// modify the array so that it can hold exactly `new_capacity` items.
419 /// Invalidates element pointers if additional memory is needed.450 /// Invalidates element pointers if additional memory is needed.
420 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {451 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {
452 self.pointer_stability.lock();
453 defer self.pointer_stability.unlock();
454
421 if (@sizeOf(T) == 0) {455 if (@sizeOf(T) == 0) {
422 self.capacity = math.maxInt(usize);456 self.capacity = math.maxInt(usize);
423 return;457 return;
...@@ -523,6 +557,8 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -523,6 +557,8 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
523 /// Invalidates element pointers to the removed element, if any.557 /// Invalidates element pointers to the removed element, if any.
524 pub fn pop(self: *Self) ?T {558 pub fn pop(self: *Self) ?T {
525 if (self.items.len == 0) return null;559 if (self.items.len == 0) return null;
560 self.pointer_stability.lock();
561 defer self.pointer_stability.unlock();
526 const val = self.items[self.items.len - 1];562 const val = self.items[self.items.len - 1];
527 self.items[self.items.len - 1] = undefined;563 self.items[self.items.len - 1] = undefined;
528 self.items.len -= 1;564 self.items.len -= 1;
...@@ -584,6 +620,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -584,6 +620,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
584 /// additional memory.620 /// additional memory.
585 capacity: usize,621 capacity: usize,
586622
623 /// Used to detect memory safety violations.
624 pointer_stability: debug.SafetyLock = .{},
625
587 /// An ArrayList containing no elements.626 /// An ArrayList containing no elements.
588 pub const empty: Self = .{627 pub const empty: Self = .{
589 .items = &.{},628 .items = &.{},
...@@ -618,10 +657,28 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -618,10 +657,28 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
618657
619 /// Release all allocated memory.658 /// Release all allocated memory.
620 pub fn deinit(self: *Self, gpa: Allocator) void {659 pub fn deinit(self: *Self, gpa: Allocator) void {
660 self.pointer_stability.assertUnlocked();
621 gpa.free(self.allocatedSlice());661 gpa.free(self.allocatedSlice());
622 self.* = undefined;662 self.* = undefined;
623 }663 }
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
625 /// Convert this list into an analogous memory-managed one.682 /// Convert this list into an analogous memory-managed one.
626 /// The returned list has ownership of the underlying memory.683 /// The returned list has ownership of the underlying memory.
627 pub fn toManaged(self: *Self, gpa: Allocator) AlignedManaged(T, alignment) {684 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 {...@@ -718,6 +775,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
718 ///775 ///
719 /// Asserts that the index is in bounds or equal to the length.776 /// Asserts that the index is in bounds or equal to the length.
720 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {777 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
778 self.pointer_stability.lock();
779 defer self.pointer_stability.unlock();
780
721 assert(self.items.len < self.capacity);781 assert(self.items.len < self.capacity);
722 self.items.len += 1;782 self.items.len += 1;
723783
...@@ -763,11 +823,12 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -763,11 +823,12 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
763 /// `undefined` values. Returns a slice pointing to the newly allocated823 /// `undefined` values. Returns a slice pointing to the newly allocated
764 /// elements, which becomes invalid after various `ArrayList`824 /// elements, which becomes invalid after various `ArrayList`
765 /// operations.825 /// operations.
766 /// Invalidates pre-existing pointers to elements at and after `index`, but
767 /// does not invalidate any before that.
768 /// Asserts that the list has capacity for the additional items.826 /// Asserts that the list has capacity for the additional items.
769 /// Asserts that the index is in bounds or equal to the length.827 /// Asserts that the index is in bounds or equal to the length.
770 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {828 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
829 self.pointer_stability.lock();
830 defer self.pointer_stability.unlock();
831
771 const new_len = self.items.len + count;832 const new_len = self.items.len + count;
772 assert(self.capacity >= new_len);833 assert(self.capacity >= new_len);
773 const to_move = self.items[index..];834 const to_move = self.items[index..];
...@@ -783,9 +844,6 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -783,9 +844,6 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
783 /// allocated elements, which becomes invalid after various `ArrayList`844 /// allocated elements, which becomes invalid after various `ArrayList`
784 /// operations.845 /// operations.
785 ///846 ///
786 /// Invalidates pre-existing pointers to elements at and after `index`, but
787 /// does not invalidate any before that.
788 ///
789 /// If the list lacks unused capacity for the additional items, returns847 /// If the list lacks unused capacity for the additional items, returns
790 /// `error.OutOfMemory`.848 /// `error.OutOfMemory`.
791 ///849 ///
...@@ -967,6 +1025,8 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -967,6 +1025,8 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
967 /// This operation is O(1).1025 /// This operation is O(1).
968 /// Asserts that the index is in bounds.1026 /// Asserts that the index is in bounds.
969 pub fn swapRemove(self: *Self, i: usize) T {1027 pub fn swapRemove(self: *Self, i: usize) T {
1028 self.pointer_stability.lock();
1029 defer self.pointer_stability.unlock();
970 const val = self.items[i];1030 const val = self.items[i];
971 self.items[i] = self.items[self.items.len - 1];1031 self.items[i] = self.items[self.items.len - 1];
972 self.items[self.items.len - 1] = undefined;1032 self.items[self.items.len - 1] = undefined;
...@@ -1128,6 +1188,8 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -1128,6 +1188,8 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
1128 /// Asserts that the new length is less than or equal to the previous length.1188 /// Asserts that the new length is less than or equal to the previous length.
1129 /// If succeds capacity is guaranteed to be equal to the length.1189 /// If succeds capacity is guaranteed to be equal to the length.
1130 pub fn shrinkAndFreePrecise(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void {1190 pub fn shrinkAndFreePrecise(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void {
1191 self.pointer_stability.lock();
1192 defer self.pointer_stability.unlock();
1131 assert(new_len <= self.items.len);1193 assert(new_len <= self.items.len);
11321194
1133 if (@sizeOf(T) == 0) {1195 if (@sizeOf(T) == 0) {
...@@ -1181,6 +1243,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -1181,6 +1243,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
1181 /// Keeps capacity the same.1243 /// Keeps capacity the same.
1182 /// Asserts that the new length is less than or equal to the previous length.1244 /// Asserts that the new length is less than or equal to the previous length.
1183 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {1245 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
1246 self.pointer_stability.lock();
1247 defer self.pointer_stability.unlock();
1248
1184 assert(new_len <= self.items.len);1249 assert(new_len <= self.items.len);
1185 @memset(self.items[new_len..], undefined);1250 @memset(self.items[new_len..], undefined);
1186 self.items.len = new_len;1251 self.items.len = new_len;
...@@ -1189,12 +1254,16 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -1189,12 +1254,16 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
1189 /// Reduce length to 0.1254 /// Reduce length to 0.
1190 /// Invalidates all element pointers.1255 /// Invalidates all element pointers.
1191 pub fn clearRetainingCapacity(self: *Self) void {1256 pub fn clearRetainingCapacity(self: *Self) void {
1257 self.pointer_stability.lock();
1258 defer self.pointer_stability.unlock();
1192 @memset(self.items, undefined);1259 @memset(self.items, undefined);
1193 self.items.len = 0;1260 self.items.len = 0;
1194 }1261 }
11951262
1196 /// Invalidates all element pointers.1263 /// Invalidates all element pointers.
1197 pub fn clearAndFree(self: *Self, gpa: Allocator) void {1264 pub fn clearAndFree(self: *Self, gpa: Allocator) void {
1265 self.pointer_stability.lock();
1266 defer self.pointer_stability.unlock();
1198 gpa.free(self.allocatedSlice());1267 gpa.free(self.allocatedSlice());
1199 self.items.len = 0;1268 self.items.len = 0;
1200 self.capacity = 0;1269 self.capacity = 0;
...@@ -1212,6 +1281,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -1212,6 +1281,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
1212 /// modify the array so that it can hold exactly `new_capacity` items.1281 /// modify the array so that it can hold exactly `new_capacity` items.
1213 /// Invalidates element pointers if additional memory is needed.1282 /// Invalidates element pointers if additional memory is needed.
1214 pub fn ensureTotalCapacityPrecise(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {1283 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
1215 if (@sizeOf(T) == 0) {1287 if (@sizeOf(T) == 0) {
1216 self.capacity = math.maxInt(usize);1288 self.capacity = math.maxInt(usize);
1217 return;1289 return;
...@@ -1371,6 +1443,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -1371,6 +1443,9 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
1371 /// Invalidates pointers to last element.1443 /// Invalidates pointers to last element.
1372 pub fn pop(self: *Self) ?T {1444 pub fn pop(self: *Self) ?T {
1373 if (self.items.len == 0) return null;1445 if (self.items.len == 0) return null;
1446 self.pointer_stability.lock();
1447 defer self.pointer_stability.unlock();
1448
1374 const val = self.items[self.items.len - 1];1449 const val = self.items[self.items.len - 1];
1375 self.items[self.items.len - 1] = undefined;1450 self.items[self.items.len - 1] = undefined;
1376 self.items.len -= 1;1451 self.items.len -= 1;