authorgravatar for 119271574+notcancername@users.noreply.github.comnotcancername <119271574+notcancername@users.noreply.github.com> 2024-01-07 05:09:54+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-15 23:44:36-07:00
log69461bcae4a78c835bdfe0aae85524342c0f8461
tree920b81234bdd9b0fdc2947ce800e95d1cd372e91
parent32e88251e48d9f4a412b08acbd04d5694ec91e19

std.array_list: Document and reduce illegal behavior in ArrayLists


1 files changed, 155 insertions(+), 78 deletions(-)

lib/std/array_list.zig+155-78
......@@ -128,7 +128,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
128128
129129 /// The caller owns the returned memory. Empties this ArrayList.
130130 pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) {
131 try self.ensureTotalCapacityPrecise(self.items.len + 1);
131 try self.ensureTotalCapacityPrecise(try addOrOom(self.items.len, 1));
132132 self.appendAssumeCapacity(sentinel);
133133 const result = try self.toOwnedSlice();
134134 return result[0 .. result.len - 1 :sentinel];
......@@ -141,25 +141,27 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
141141 return cloned;
142142 }
143143
144 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
145 /// If `n` is equal to the length of the list this operation is equivalent to append.
144 /// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
145 /// If `i` is equal to the length of the list this operation is equivalent to append.
146146 /// This operation is O(N).
147147 /// Invalidates pointers if additional memory is needed.
148 pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void {
149 const dst = try self.addManyAt(n, 1);
148 /// **Asserts that `i <= self.items.len`.**
149 pub fn insert(self: *Self, i: usize, item: T) Allocator.Error!void {
150 const dst = try self.addManyAt(i, 1);
150151 dst[0] = item;
151152 }
152153
153 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
154 /// If `n` is equal to the length of the list this operation is equivalent to append.
154 /// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
155 /// If `i` is equal to the length of the list this operation is equivalent to appendAssumeCapacity.
155156 /// This operation is O(N).
156 /// Asserts that there is enough capacity for the new item.
157 pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void {
157 /// **Asserts that `i <= self.items.len`.**
158 /// **Asserts that `self.items.len < self.capacity` .**
159 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
158160 assert(self.items.len < self.capacity);
159161 self.items.len += 1;
160162
161 mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
162 self.items[n] = item;
163 mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
164 self.items[i] = item;
163165 }
164166
165167 /// Add `count` new elements at position `index`, which have
......@@ -169,8 +171,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
169171 /// Invalidates pre-existing pointers to elements at and after `index`.
170172 /// Invalidates all pre-existing element pointers if capacity must be
171173 /// increased to accomodate the new elements.
174 /// **Asserts that `index <= self.items.len`.**
172175 pub fn addManyAt(self: *Self, index: usize, count: usize) Allocator.Error![]T {
173 const new_len = self.items.len + count;
176 const new_len = try addOrOom(self.items.len, count);
174177
175178 if (self.capacity >= new_len)
176179 return addManyAtAssumeCapacity(self, index, count);
......@@ -205,9 +208,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
205208 /// `undefined` values. Returns a slice pointing to the newly allocated
206209 /// elements, which becomes invalid after various `ArrayList`
207210 /// operations.
208 /// Asserts that there is enough capacity for the new elements.
209211 /// Invalidates pre-existing pointers to elements at and after `index`, but
210212 /// does not invalidate any before that.
213 /// **Asserts that `index <= self.items.len`.**
214 /// **Asserts that the list can hold `count` additional items.**
211215 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
212216 const new_len = self.items.len + count;
213217 assert(self.capacity >= new_len);
......@@ -224,6 +228,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
224228 /// Invalidates pre-existing pointers to elements at and after `index`.
225229 /// Invalidates all pre-existing element pointers if capacity must be
226230 /// increased to accomodate the new elements.
231 /// **Asserts that `index <= self.items.len`.**
227232 pub fn insertSlice(
228233 self: *Self,
229234 index: usize,
......@@ -237,8 +242,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
237242 /// Grows list if `len < new_items.len`.
238243 /// Shrinks list if `len > new_items.len`.
239244 /// Invalidates pointers if this ArrayList is resized.
245 /// **Asserts that `start <= self.items.len`.**
240246 pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: []const T) Allocator.Error!void {
241 const after_range = start + len;
247 const after_range = try addOrOom(start, len);
242248 const range = self.items[start..after_range];
243249
244250 if (range.len == new_items.len)
......@@ -251,7 +257,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
251257 try self.insertSlice(after_range, rest);
252258 } else {
253259 @memcpy(range[0..new_items.len], new_items);
254 const after_subrange = start + new_items.len;
260 const after_subrange = try addOrOom(start, new_items.len);
255261
256262 for (self.items[after_range..], 0..) |item, i| {
257263 self.items[after_subrange..][i] = item;
......@@ -261,16 +267,16 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
261267 }
262268 }
263269
264 /// Extend the list by 1 element. Allocates more memory as necessary.
270 /// Extends the list by 1 element. Allocates more memory as necessary.
265271 /// Invalidates pointers if additional memory is needed.
266272 pub fn append(self: *Self, item: T) Allocator.Error!void {
267273 const new_item_ptr = try self.addOne();
268274 new_item_ptr.* = item;
269275 }
270276
271 /// Extend the list by 1 element, but assert `self.capacity`
272 /// is sufficient to hold an additional item. **Does not**
277 /// Extends the list by 1 element. Does not
273278 /// invalidate pointers.
279 /// **Asserts that the list can hold one additional item.**
274280 pub fn appendAssumeCapacity(self: *Self, item: T) void {
275281 const new_item_ptr = self.addOneAssumeCapacity();
276282 new_item_ptr.* = item;
......@@ -278,10 +284,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
278284
279285 /// Remove the element at index `i`, shift elements after index
280286 /// `i` forward, and return the removed element.
281 /// Asserts the array has at least one item.
282287 /// Invalidates pointers to end of list.
283288 /// This operation is O(N).
284289 /// This preserves item order. Use `swapRemove` if order preservation is not important.
290 /// **Asserts that `i < self.items.len`.**
291 /// **Asserts that the list is not empty.**
285292 pub fn orderedRemove(self: *Self, i: usize) T {
286293 const newlen = self.items.len - 1;
287294 if (newlen == i) return self.pop();
......@@ -297,6 +304,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
297304 /// The empty slot is filled from the end of the list.
298305 /// This operation is O(1).
299306 /// This may not preserve item order. Use `orderedRemove` if you need to preserve order.
307 /// **Asserts that `i < self.items.len`.**
308 /// **Asserts that the list is not empty.**
300309 pub fn swapRemove(self: *Self, i: usize) T {
301310 if (self.items.len - 1 == i) return self.pop();
302311
......@@ -313,8 +322,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
313322 self.appendSliceAssumeCapacity(items);
314323 }
315324
316 /// Append the slice of items to the list, asserting the capacity is already
317 /// enough to store the new items. **Does not** invalidate pointers.
325 /// Append the slice of items to the list. Does not invalidate pointers.
326 /// **Asserts that the list can hold `items.len` additional items.**
318327 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
319328 const old_len = self.items.len;
320329 const new_len = old_len + items.len;
......@@ -332,10 +341,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
332341 self.appendUnalignedSliceAssumeCapacity(items);
333342 }
334343
335 /// Append the slice of items to the list, asserting the capacity is already
336 /// enough to store the new items. **Does not** invalidate pointers.
344 /// Append the slice of items to the list. **Does not** invalidate pointers.
337345 /// Only call this function if calling `appendSliceAssumeCapacity` instead
338346 /// would be a compile error.
347 /// **Asserts that the list can hold `items.len` additional items.**
339348 pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void {
340349 const old_len = self.items.len;
341350 const new_len = old_len + items.len;
......@@ -348,7 +357,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
348357 @compileError("The Writer interface is only defined for ArrayList(u8) " ++
349358 "but the given type is ArrayList(" ++ @typeName(T) ++ ")")
350359 else
351 std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
360 std.io.Writer(*Self, Allocator.Error, appendWrite);
352361
353362 /// Initializes a Writer which will append to the list.
354363 pub fn writer(self: *Self) Writer {
......@@ -370,14 +379,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
370379 /// have a more optimal memset codegen in case it has a repeated byte pattern.
371380 pub inline fn appendNTimes(self: *Self, value: T, n: usize) Allocator.Error!void {
372381 const old_len = self.items.len;
373 try self.resize(self.items.len + n);
382 try self.resize(try addOrOom(self.items.len, n));
374383 @memset(self.items[old_len..self.items.len], value);
375384 }
376385
377386 /// Append a value to the list `n` times.
378 /// Asserts the capacity is enough. **Does not** invalidate pointers.
387 /// Does not invalidate pointers.
379388 /// The function is inline so that a comptime-known `value` parameter will
380389 /// have a more optimal memset codegen in case it has a repeated byte pattern.
390 /// **Asserts that the list can hold `n` additional items.**
381391 pub inline fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
382392 const new_len = self.items.len + n;
383393 assert(new_len <= self.capacity);
......@@ -395,6 +405,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
395405
396406 /// Reduce allocated capacity to `new_len`.
397407 /// May invalidate element pointers.
408 /// **Asserts that `new_len <= self.items.len`.**
398409 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
399410 var unmanaged = self.moveToUnmanaged();
400411 unmanaged.shrinkAndFree(self.allocator, new_len);
......@@ -403,6 +414,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
403414
404415 /// Reduce length to `new_len`.
405416 /// Invalidates pointers for the elements `items[new_len..]`.
417 /// **Asserts that `new_len <= self.items.len`.**
406418 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
407419 assert(new_len <= self.items.len);
408420 self.items.len = new_len;
......@@ -466,7 +478,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
466478 /// Modify the array so that it can hold at least `additional_count` **more** items.
467479 /// Invalidates pointers if additional memory is needed.
468480 pub fn ensureUnusedCapacity(self: *Self, additional_count: usize) Allocator.Error!void {
469 return self.ensureTotalCapacity(self.items.len + additional_count);
481 return self.ensureTotalCapacity(try addOrOom(self.items.len, additional_count));
470482 }
471483
472484 /// Increases the array's length to match the full capacity that is already allocated.
......@@ -478,14 +490,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
478490 /// Increase length by 1, returning pointer to the new item.
479491 /// The returned pointer becomes invalid when the list resized.
480492 pub fn addOne(self: *Self) Allocator.Error!*T {
481 try self.ensureTotalCapacity(self.items.len + 1);
493 try self.ensureUnusedCapacity(1);
482494 return self.addOneAssumeCapacity();
483495 }
484496
485497 /// Increase length by 1, returning pointer to the new item.
486 /// Asserts that there is already space for the new item without allocating more.
487498 /// The returned pointer becomes invalid when the list is resized.
488 /// **Does not** invalidate element pointers.
499 /// Does not invalidate element pointers.
500 /// **Asserts that the list can hold one additional item.**
489501 pub fn addOneAssumeCapacity(self: *Self) *T {
490502 assert(self.items.len < self.capacity);
491503 self.items.len += 1;
......@@ -498,15 +510,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
498510 /// Resizes list if `self.capacity` is not large enough.
499511 pub fn addManyAsArray(self: *Self, comptime n: usize) Allocator.Error!*[n]T {
500512 const prev_len = self.items.len;
501 try self.resize(self.items.len + n);
513 try self.resize(try addOrOom(self.items.len, n));
502514 return self.items[prev_len..][0..n];
503515 }
504516
505517 /// Resize the array, adding `n` new elements, which have `undefined` values.
506518 /// The return value is an array pointing to the newly allocated elements.
507 /// Asserts that there is already space for the new item without allocating more.
508 /// **Does not** invalidate element pointers.
519 /// Does not invalidate element pointers.
509520 /// The returned pointer becomes invalid when the list is resized.
521 /// **Asserts that the list can hold `n` additional items.**
510522 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
511523 assert(self.items.len + n <= self.capacity);
512524 const prev_len = self.items.len;
......@@ -520,15 +532,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
520532 /// Resizes list if `self.capacity` is not large enough.
521533 pub fn addManyAsSlice(self: *Self, n: usize) Allocator.Error![]T {
522534 const prev_len = self.items.len;
523 try self.resize(self.items.len + n);
535 try self.resize(try addOrOom(self.items.len, n));
524536 return self.items[prev_len..][0..n];
525537 }
526538
527539 /// Resize the array, adding `n` new elements, which have `undefined` values.
528540 /// The return value is a slice pointing to the newly allocated elements.
529 /// Asserts that there is already space for the new item without allocating more.
530 /// **Does not** invalidate element pointers.
541 /// Does not invalidate element pointers.
531542 /// The returned pointer becomes invalid when the list is resized.
543 /// **Asserts that the list can hold `n` additional items.**
532544 pub fn addManyAsSliceAssumeCapacity(self: *Self, n: usize) []T {
533545 assert(self.items.len + n <= self.capacity);
534546 const prev_len = self.items.len;
......@@ -537,8 +549,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
537549 }
538550
539551 /// Remove and return the last element from the list.
540 /// Asserts the list has at least one item.
541552 /// Invalidates pointers to the removed element.
553 /// **Asserts that the list is not empty.**
542554 pub fn pop(self: *Self) T {
543555 const val = self.items[self.items.len - 1];
544556 self.items.len -= 1;
......@@ -568,15 +580,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
568580 return self.allocatedSlice()[self.items.len..];
569581 }
570582
571 /// Return the last element from the list.
572 /// Asserts the list has at least one item.
583 /// Returns the last element from the list.
584 /// **Asserts that the list is not empty.**
573585 pub fn getLast(self: Self) T {
574586 const val = self.items[self.items.len - 1];
575587 return val;
576588 }
577589
578 /// Return the last element from the list, or
579 /// return `null` if list is empty.
590 /// Returns the last element from the list, or `null` if list is empty.
580591 pub fn getLastOrNull(self: Self) ?T {
581592 if (self.items.len == 0) return null;
582593 return self.getLast();
......@@ -635,8 +646,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
635646
636647 /// Initialize with externally-managed memory. The buffer determines the
637648 /// capacity, and the length is set to zero.
638 /// When initialized this way, all methods that accept an Allocator
639 /// argument are illegal to call.
649 /// **When initialized this way, all methods that accept an Allocator
650 /// argument cause illegal behavior**.
640651 pub fn initBuffer(buffer: Slice) Self {
641652 return .{
642653 .items = buffer[0..0],
......@@ -695,7 +706,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
695706
696707 /// The caller owns the returned memory. ArrayList becomes empty.
697708 pub fn toOwnedSliceSentinel(self: *Self, allocator: Allocator, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) {
698 try self.ensureTotalCapacityPrecise(allocator, self.items.len + 1);
709 try self.ensureTotalCapacityPrecise(allocator, try addOrOom(self.items.len, 1));
699710 self.appendAssumeCapacity(sentinel);
700711 const result = try self.toOwnedSlice(allocator);
701712 return result[0 .. result.len - 1 :sentinel];
......@@ -708,25 +719,27 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
708719 return cloned;
709720 }
710721
711 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
712 /// If `n` is equal to the length of the list this operation is equivalent to append.
722 /// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
723 /// If `i` is equal to the length of the list this operation is equivalent to append.
713724 /// This operation is O(N).
714725 /// Invalidates pointers if additional memory is needed.
715 pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void {
716 const dst = try self.addManyAt(allocator, n, 1);
726 /// **Asserts that `i < self.items.len`.**
727 pub fn insert(self: *Self, allocator: Allocator, i: usize, item: T) Allocator.Error!void {
728 const dst = try self.addManyAt(allocator, i, 1);
717729 dst[0] = item;
718730 }
719731
720 /// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
721 /// If `n` is equal to the length of the list this operation is equivalent to append.
732 /// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
733 /// If in` is equal to the length of the list this operation is equivalent to append.
722734 /// This operation is O(N).
723 /// Asserts that there is enough capacity for the new item.
724 pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void {
735 /// **Asserts that `i < self.items.len`.**
736 /// **Asserts that the list can hold one additional item.**
737 pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
725738 assert(self.items.len < self.capacity);
726739 self.items.len += 1;
727740
728 mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
729 self.items[n] = item;
741 mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
742 self.items[i] = item;
730743 }
731744
732745 /// Add `count` new elements at position `index`, which have
......@@ -736,6 +749,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
736749 /// Invalidates pre-existing pointers to elements at and after `index`.
737750 /// Invalidates all pre-existing element pointers if capacity must be
738751 /// increased to accomodate the new elements.
752 /// **Asserts that `index <= self.items.len`.**
739753 pub fn addManyAt(
740754 self: *Self,
741755 allocator: Allocator,
......@@ -751,9 +765,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
751765 /// `undefined` values. Returns a slice pointing to the newly allocated
752766 /// elements, which becomes invalid after various `ArrayList`
753767 /// operations.
754 /// Asserts that there is enough capacity for the new elements.
755768 /// Invalidates pre-existing pointers to elements at and after `index`, but
756769 /// does not invalidate any before that.
770 /// **Asserts that `index <= self.items.len`.**
771 /// **Asserts that the list can hold `count` additional items.**
757772 pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
758773 const new_len = self.items.len + count;
759774 assert(self.capacity >= new_len);
......@@ -770,6 +785,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
770785 /// Invalidates pre-existing pointers to elements at and after `index`.
771786 /// Invalidates all pre-existing element pointers if capacity must be
772787 /// increased to accomodate the new elements.
788 /// **Asserts that `index <= self.items.len`.**
773789 pub fn insertSlice(
774790 self: *Self,
775791 allocator: Allocator,
......@@ -788,6 +804,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
788804 /// Grows list if `len < new_items.len`.
789805 /// Shrinks list if `len > new_items.len`
790806 /// Invalidates pointers if this ArrayList is resized.
807 /// **Asserts that `start <= self.items.len`.**
791808 pub fn replaceRange(
792809 self: *Self,
793810 allocator: Allocator,
......@@ -807,17 +824,18 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
807824 new_item_ptr.* = item;
808825 }
809826
810 /// Extend the list by 1 element, but asserting `self.capacity`
811 /// is sufficient to hold an additional item.
827 /// Extend the list by 1 element.
828 /// **Asserts that the list can hold one additional item.**
812829 pub fn appendAssumeCapacity(self: *Self, item: T) void {
813830 const new_item_ptr = self.addOneAssumeCapacity();
814831 new_item_ptr.* = item;
815832 }
816833
817834 /// Remove the element at index `i` from the list and return its value.
818 /// Asserts the array has at least one item. Invalidates pointers to
819 /// last element.
835 /// Invalidates pointers to the last element.
820836 /// This operation is O(N).
837 /// **Asserts that `i < self.items.len`.**
838 /// **Asserts that the list is not empty.**
821839 pub fn orderedRemove(self: *Self, i: usize) T {
822840 const newlen = self.items.len - 1;
823841 if (newlen == i) return self.pop();
......@@ -833,6 +851,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
833851 /// The empty slot is filled from the end of the list.
834852 /// Invalidates pointers to last element.
835853 /// This operation is O(1).
854 /// **Asserts that `i < self.items.len`.**
855 /// **Asserts that the list is not empty.**
836856 pub fn swapRemove(self: *Self, i: usize) T {
837857 if (self.items.len - 1 == i) return self.pop();
838858
......@@ -849,8 +869,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
849869 self.appendSliceAssumeCapacity(items);
850870 }
851871
852 /// Append the slice of items to the list, asserting the capacity is enough
853 /// to store the new items.
872 /// Append the slice of items to the list.
873 /// **Asserts that the list can hold `items.len` additional items.**
854874 pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
855875 const old_len = self.items.len;
856876 const new_len = old_len + items.len;
......@@ -868,9 +888,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
868888 self.appendUnalignedSliceAssumeCapacity(items);
869889 }
870890
871 /// Append an unaligned slice of items to the list, asserting the capacity is enough
872 /// to store the new items. Only call this function if a call to `appendSliceAssumeCapacity`
891 /// Append an unaligned slice of items to the list.
892 /// Only call this function if a call to `appendSliceAssumeCapacity`
873893 /// instead would be a compile error.
894 /// **Asserts that the list can hold `items.len` additional items.**
874895 pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void {
875896 const old_len = self.items.len;
876897 const new_len = old_len + items.len;
......@@ -888,7 +909,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
888909 @compileError("The Writer interface is only defined for ArrayList(u8) " ++
889910 "but the given type is ArrayList(" ++ @typeName(T) ++ ")")
890911 else
891 std.io.Writer(WriterContext, error{OutOfMemory}, appendWrite);
912 std.io.Writer(WriterContext, Allocator.Error, appendWrite);
892913
893914 /// Initializes a Writer which will append to the list.
894915 pub fn writer(self: *Self, allocator: Allocator) Writer {
......@@ -910,15 +931,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
910931 /// have a more optimal memset codegen in case it has a repeated byte pattern.
911932 pub inline fn appendNTimes(self: *Self, allocator: Allocator, value: T, n: usize) Allocator.Error!void {
912933 const old_len = self.items.len;
913 try self.resize(allocator, self.items.len + n);
934 try self.resize(allocator, try addOrOom(self.items.len, n));
914935 @memset(self.items[old_len..self.items.len], value);
915936 }
916937
917938 /// Append a value to the list `n` times.
918939 /// **Does not** invalidate pointers.
919 /// Asserts the capacity is enough.
920940 /// The function is inline so that a comptime-known `value` parameter will
921 /// have a more optimal memset codegen in case it has a repeated byte pattern.
941 /// have better memset codegen in case it has a repeated byte pattern.
942 /// **Asserts that the list can hold `n` additional items.**
922943 pub inline fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
923944 const new_len = self.items.len + n;
924945 assert(new_len <= self.capacity);
......@@ -936,6 +957,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
936957
937958 /// Reduce allocated capacity to `new_len`.
938959 /// May invalidate element pointers.
960 /// **Asserts that `new_len <= self.items.len`.**
939961 pub fn shrinkAndFree(self: *Self, allocator: Allocator, new_len: usize) void {
940962 assert(new_len <= self.items.len);
941963
......@@ -968,6 +990,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
968990 /// Reduce length to `new_len`.
969991 /// Invalidates pointers to elements `items[new_len..]`.
970992 /// Keeps capacity the same.
993 /// **Asserts that `new_len <= self.items.len`.**
971994 pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
972995 assert(new_len <= self.items.len);
973996 self.items.len = new_len;
......@@ -1030,12 +1053,12 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
10301053 allocator: Allocator,
10311054 additional_count: usize,
10321055 ) Allocator.Error!void {
1033 return self.ensureTotalCapacity(allocator, self.items.len + additional_count);
1056 return self.ensureTotalCapacity(allocator, try addOrOom(self.items.len, additional_count));
10341057 }
10351058
10361059 /// Increases the array's length to match the full capacity that is already allocated.
10371060 /// The new elements have `undefined` values.
1038 /// **Does not** invalidate pointers.
1061 /// Does not invalidate pointers.
10391062 pub fn expandToCapacity(self: *Self) void {
10401063 self.items.len = self.capacity;
10411064 }
......@@ -1043,15 +1066,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
10431066 /// Increase length by 1, returning pointer to the new item.
10441067 /// The returned pointer becomes invalid when the list resized.
10451068 pub fn addOne(self: *Self, allocator: Allocator) Allocator.Error!*T {
1046 const newlen = self.items.len + 1;
1069 const newlen = try addOrOom(self.items.len, 1);
10471070 try self.ensureTotalCapacity(allocator, newlen);
10481071 return self.addOneAssumeCapacity();
10491072 }
10501073
10511074 /// Increase length by 1, returning pointer to the new item.
1052 /// Asserts that there is already space for the new item without allocating more.
10531075 /// **Does not** invalidate pointers.
10541076 /// The returned pointer becomes invalid when the list resized.
1077 /// **Asserts that the list can hold one additional item.**
10551078 pub fn addOneAssumeCapacity(self: *Self) *T {
10561079 assert(self.items.len < self.capacity);
10571080
......@@ -1064,15 +1087,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
10641087 /// The returned pointer becomes invalid when the list is resized.
10651088 pub fn addManyAsArray(self: *Self, allocator: Allocator, comptime n: usize) Allocator.Error!*[n]T {
10661089 const prev_len = self.items.len;
1067 try self.resize(allocator, self.items.len + n);
1090 try self.resize(allocator, try addOrOom(self.items.len, n));
10681091 return self.items[prev_len..][0..n];
10691092 }
10701093
10711094 /// Resize the array, adding `n` new elements, which have `undefined` values.
10721095 /// The return value is an array pointing to the newly allocated elements.
1073 /// Asserts that there is already space for the new item without allocating more.
10741096 /// **Does not** invalidate pointers.
10751097 /// The returned pointer becomes invalid when the list is resized.
1098 /// **Asserts that the list can hold `n` additional items.**
10761099 pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
10771100 assert(self.items.len + n <= self.capacity);
10781101 const prev_len = self.items.len;
......@@ -1086,15 +1109,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
10861109 /// Resizes list if `self.capacity` is not large enough.
10871110 pub fn addManyAsSlice(self: *Self, allocator: Allocator, n: usize) Allocator.Error![]T {
10881111 const prev_len = self.items.len;
1089 try self.resize(allocator, self.items.len + n);
1112 try self.resize(allocator, try addOrOom(self.items.len, n));
10901113 return self.items[prev_len..][0..n];
10911114 }
10921115
10931116 /// Resize the array, adding `n` new elements, which have `undefined` values.
10941117 /// The return value is a slice pointing to the newly allocated elements.
1095 /// Asserts that there is already space for the new item without allocating more.
1096 /// **Does not** invalidate element pointers.
1118 /// Does not invalidate element pointers.
10971119 /// The returned pointer becomes invalid when the list is resized.
1120 /// **Asserts that the list can hold `n` additional items.**
10981121 pub fn addManyAsSliceAssumeCapacity(self: *Self, n: usize) []T {
10991122 assert(self.items.len + n <= self.capacity);
11001123 const prev_len = self.items.len;
......@@ -1103,8 +1126,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
11031126 }
11041127
11051128 /// Remove and return the last element from the list.
1106 /// Asserts the list has at least one item.
11071129 /// Invalidates pointers to last element.
1130 /// **Asserts that the list is not empty.**
11081131 pub fn pop(self: *Self) T {
11091132 const val = self.items[self.items.len - 1];
11101133 self.items.len -= 1;
......@@ -1134,7 +1157,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
11341157 }
11351158
11361159 /// Return the last element from the list.
1137 /// Asserts the list has at least one item.
1160 /// **Asserts that the list is not empty.**
11381161 pub fn getLast(self: Self) T {
11391162 const val = self.items[self.items.len - 1];
11401163 return val;
......@@ -1160,6 +1183,14 @@ fn growCapacity(current: usize, minimum: usize) usize {
11601183 }
11611184}
11621185
1186/// Adds a and b, returning `error.OutOfMemory` if overflow occurred.
1187/// This is equivalent to `math.add`. See #18467 for why it is used.
1188fn addOrOom(a: usize, b: usize) error{OutOfMemory}!usize {
1189 const ov = @addWithOverflow(a, b);
1190 if (ov[1] != 0) return error.OutOfMemory;
1191 return ov[0];
1192}
1193
11631194test "std.ArrayList/ArrayListUnmanaged.init" {
11641195 {
11651196 var list = ArrayList(i32).init(testing.allocator);
......@@ -1952,3 +1983,49 @@ test "std.ArrayList(u32).getLastOrNull()" {
19521983 const const_list = list;
19531984 try testing.expectEqual(const_list.getLastOrNull().?, 2);
19541985}
1986
1987test "return OutOfMemory when capacity would exceed maximum usize integer value" {
1988 // Because a portable way to create maxInt(usize)-sized slices does not seem to exist yet, this
1989 // will have to do.
1990
1991 const a = testing.allocator;
1992
1993 var alu = ArrayListUnmanaged(u32){
1994 .items = undefined,
1995 .capacity = math.maxInt(usize),
1996 };
1997 alu.items.len = math.maxInt(usize);
1998
1999 try testing.expectError(error.OutOfMemory, alu.append(a, undefined));
2000 try testing.expectError(error.OutOfMemory, alu.appendSlice(a, &.{undefined}));
2001 try testing.expectError(error.OutOfMemory, alu.appendNTimes(a, undefined, 1));
2002 try testing.expectError(error.OutOfMemory, alu.appendUnalignedSlice(a, &.{undefined}));
2003 try testing.expectError(error.OutOfMemory, alu.addOne(a));
2004 try testing.expectError(error.OutOfMemory, alu.addManyAt(a, 0, 1));
2005 try testing.expectError(error.OutOfMemory, alu.addManyAsArray(a, 1));
2006 try testing.expectError(error.OutOfMemory, alu.addManyAsSlice(a, 1));
2007 try testing.expectError(error.OutOfMemory, alu.insert(a, 0, undefined));
2008 try testing.expectError(error.OutOfMemory, alu.insertSlice(a, 0, &.{undefined}));
2009 try testing.expectError(error.OutOfMemory, alu.toOwnedSliceSentinel(a, 0));
2010 try testing.expectError(error.OutOfMemory, alu.ensureUnusedCapacity(a, 1));
2011
2012 var al = ArrayList(u32){
2013 .items = undefined,
2014 .capacity = math.maxInt(usize),
2015 .allocator = a,
2016 };
2017 al.items.len = math.maxInt(usize);
2018
2019 try testing.expectError(error.OutOfMemory, al.append(undefined));
2020 try testing.expectError(error.OutOfMemory, al.appendSlice(&.{undefined}));
2021 try testing.expectError(error.OutOfMemory, al.appendNTimes(undefined, 1));
2022 try testing.expectError(error.OutOfMemory, al.appendUnalignedSlice(&.{undefined}));
2023 try testing.expectError(error.OutOfMemory, al.addOne());
2024 try testing.expectError(error.OutOfMemory, al.addManyAt(0, 1));
2025 try testing.expectError(error.OutOfMemory, al.addManyAsArray(1));
2026 try testing.expectError(error.OutOfMemory, al.addManyAsSlice(1));
2027 try testing.expectError(error.OutOfMemory, al.insert(0, undefined));
2028 try testing.expectError(error.OutOfMemory, al.insertSlice(0, &.{undefined}));
2029 try testing.expectError(error.OutOfMemory, al.toOwnedSliceSentinel(0));
2030 try testing.expectError(error.OutOfMemory, al.ensureUnusedCapacity(1));
2031}