Merge pull request #18468 from notcancername/legalize-arraylist
std.array_list: Document and reduce illegal behavior in ArrayLists
This commit is contained in:
@@ -10,7 +10,7 @@ const Allocator = mem.Allocator;
|
||||
/// This is a wrapper around an array of T values. Initialize with `init`.
|
||||
///
|
||||
/// This struct internally stores a `std.mem.Allocator` for memory management.
|
||||
/// To manually specify an allocator with each method call see `ArrayListUnmanaged`.
|
||||
/// To manually specify an allocator with each function call see `ArrayListUnmanaged`.
|
||||
pub fn ArrayList(comptime T: type) type {
|
||||
return ArrayListAligned(T, null);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ pub fn ArrayList(comptime T: type) type {
|
||||
/// Initialize with `init`.
|
||||
///
|
||||
/// This struct internally stores a `std.mem.Allocator` for memory management.
|
||||
/// To manually specify an allocator with each method call see `ArrayListAlignedUnmanaged`.
|
||||
/// To manually specify an allocator with each function call see `ArrayListAlignedUnmanaged`.
|
||||
pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
if (alignment) |a| {
|
||||
if (a == @alignOf(T)) {
|
||||
@@ -30,15 +30,13 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
}
|
||||
return struct {
|
||||
const Self = @This();
|
||||
/// Contents of the list. Pointers to elements in this slice are
|
||||
/// **invalid after resizing operations** on the ArrayList unless the
|
||||
/// operation explicitly either: (1) states otherwise or (2) lists the
|
||||
/// invalidated pointers.
|
||||
/// Contents of the list. This field is intended to be accessed
|
||||
/// directly.
|
||||
///
|
||||
/// The allocator used determines how element pointers are
|
||||
/// invalidated, so the behavior may vary between lists. To avoid
|
||||
/// illegal behavior, take into account the above paragraph plus the
|
||||
/// explicit statements given in each method.
|
||||
/// Pointers to elements in this slice are invalidated by various
|
||||
/// functions of this ArrayList in accordance with the respective
|
||||
/// documentation. In all cases, "invalidated" means that the memory
|
||||
/// has been passed to this allocator's resize or free function.
|
||||
items: Slice,
|
||||
/// How many T values this list can hold without allocating
|
||||
/// additional memory.
|
||||
@@ -128,7 +126,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// The caller owns the returned memory. Empties this ArrayList.
|
||||
pub fn toOwnedSliceSentinel(self: *Self, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) {
|
||||
try self.ensureTotalCapacityPrecise(self.items.len + 1);
|
||||
try self.ensureTotalCapacityPrecise(try addOrOom(self.items.len, 1));
|
||||
self.appendAssumeCapacity(sentinel);
|
||||
const result = try self.toOwnedSlice();
|
||||
return result[0 .. result.len - 1 :sentinel];
|
||||
@@ -141,25 +139,28 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
|
||||
/// If `n` is equal to the length of the list this operation is equivalent to append.
|
||||
/// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
|
||||
/// If `i` is equal to the length of the list this operation is equivalent to append.
|
||||
/// This operation is O(N).
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
pub fn insert(self: *Self, n: usize, item: T) Allocator.Error!void {
|
||||
const dst = try self.addManyAt(n, 1);
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn insert(self: *Self, i: usize, item: T) Allocator.Error!void {
|
||||
const dst = try self.addManyAt(i, 1);
|
||||
dst[0] = item;
|
||||
}
|
||||
|
||||
/// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
|
||||
/// If `n` is equal to the length of the list this operation is equivalent to append.
|
||||
/// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
|
||||
/// If `i` is equal to the length of the list this operation is
|
||||
/// equivalent to appendAssumeCapacity.
|
||||
/// This operation is O(N).
|
||||
/// Asserts that there is enough capacity for the new item.
|
||||
pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void {
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
|
||||
assert(self.items.len < self.capacity);
|
||||
self.items.len += 1;
|
||||
|
||||
mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
|
||||
self.items[n] = item;
|
||||
mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
|
||||
self.items[i] = item;
|
||||
}
|
||||
|
||||
/// Add `count` new elements at position `index`, which have
|
||||
@@ -169,8 +170,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Invalidates pre-existing pointers to elements at and after `index`.
|
||||
/// Invalidates all pre-existing element pointers if capacity must be
|
||||
/// increased to accomodate the new elements.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn addManyAt(self: *Self, index: usize, count: usize) Allocator.Error![]T {
|
||||
const new_len = self.items.len + count;
|
||||
const new_len = try addOrOom(self.items.len, count);
|
||||
|
||||
if (self.capacity >= new_len)
|
||||
return addManyAtAssumeCapacity(self, index, count);
|
||||
@@ -208,6 +210,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Asserts that there is enough capacity for the new elements.
|
||||
/// Invalidates pre-existing pointers to elements at and after `index`, but
|
||||
/// does not invalidate any before that.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
|
||||
const new_len = self.items.len + count;
|
||||
assert(self.capacity >= new_len);
|
||||
@@ -224,6 +227,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Invalidates pre-existing pointers to elements at and after `index`.
|
||||
/// Invalidates all pre-existing element pointers if capacity must be
|
||||
/// increased to accomodate the new elements.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn insertSlice(
|
||||
self: *Self,
|
||||
index: usize,
|
||||
@@ -236,9 +240,10 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Replace range of elements `list[start..][0..len]` with `new_items`.
|
||||
/// Grows list if `len < new_items.len`.
|
||||
/// Shrinks list if `len > new_items.len`.
|
||||
/// Invalidates pointers if this ArrayList is resized.
|
||||
/// Invalidates element pointers if this ArrayList is resized.
|
||||
/// Asserts that the start index is in bounds or equal to the length.
|
||||
pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: []const T) Allocator.Error!void {
|
||||
const after_range = start + len;
|
||||
const after_range = try addOrOom(start, len);
|
||||
const range = self.items[start..after_range];
|
||||
|
||||
if (range.len == new_items.len)
|
||||
@@ -251,7 +256,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
try self.insertSlice(after_range, rest);
|
||||
} else {
|
||||
@memcpy(range[0..new_items.len], new_items);
|
||||
const after_subrange = start + new_items.len;
|
||||
const after_subrange = try addOrOom(start, new_items.len);
|
||||
|
||||
for (self.items[after_range..], 0..) |item, i| {
|
||||
self.items[after_subrange..][i] = item;
|
||||
@@ -261,16 +266,16 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend the list by 1 element. Allocates more memory as necessary.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Extends the list by 1 element. Allocates more memory as necessary.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn append(self: *Self, item: T) Allocator.Error!void {
|
||||
const new_item_ptr = try self.addOne();
|
||||
new_item_ptr.* = item;
|
||||
}
|
||||
|
||||
/// Extend the list by 1 element, but assert `self.capacity`
|
||||
/// is sufficient to hold an additional item. **Does not**
|
||||
/// invalidate pointers.
|
||||
/// Extends the list by 1 element.
|
||||
/// Never invalidates element pointers.
|
||||
/// Asserts that the list can hold one additional item.
|
||||
pub fn appendAssumeCapacity(self: *Self, item: T) void {
|
||||
const new_item_ptr = self.addOneAssumeCapacity();
|
||||
new_item_ptr.* = item;
|
||||
@@ -278,10 +283,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// Remove the element at index `i`, shift elements after index
|
||||
/// `i` forward, and return the removed element.
|
||||
/// Asserts the array has at least one item.
|
||||
/// Invalidates pointers to end of list.
|
||||
/// Invalidates element pointers to end of list.
|
||||
/// This operation is O(N).
|
||||
/// This preserves item order. Use `swapRemove` if order preservation is not important.
|
||||
/// Asserts that the index is in bounds.
|
||||
/// Asserts that the list is not empty.
|
||||
pub fn orderedRemove(self: *Self, i: usize) T {
|
||||
const newlen = self.items.len - 1;
|
||||
if (newlen == i) return self.pop();
|
||||
@@ -297,6 +303,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// The empty slot is filled from the end of the list.
|
||||
/// This operation is O(1).
|
||||
/// This may not preserve item order. Use `orderedRemove` if you need to preserve order.
|
||||
/// Asserts that the list is not empty.
|
||||
/// Asserts that the index is in bounds.
|
||||
pub fn swapRemove(self: *Self, i: usize) T {
|
||||
if (self.items.len - 1 == i) return self.pop();
|
||||
|
||||
@@ -307,14 +315,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// Append the slice of items to the list. Allocates more
|
||||
/// memory as necessary.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn appendSlice(self: *Self, items: []const T) Allocator.Error!void {
|
||||
try self.ensureUnusedCapacity(items.len);
|
||||
self.appendSliceAssumeCapacity(items);
|
||||
}
|
||||
|
||||
/// Append the slice of items to the list, asserting the capacity is already
|
||||
/// enough to store the new items. **Does not** invalidate pointers.
|
||||
/// Append the slice of items to the list.
|
||||
/// Never invalidates element pointers.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
|
||||
const old_len = self.items.len;
|
||||
const new_len = old_len + items.len;
|
||||
@@ -326,16 +335,18 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Append an unaligned slice of items to the list. Allocates more
|
||||
/// memory as necessary. Only call this function if calling
|
||||
/// `appendSlice` instead would be a compile error.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn appendUnalignedSlice(self: *Self, items: []align(1) const T) Allocator.Error!void {
|
||||
try self.ensureUnusedCapacity(items.len);
|
||||
self.appendUnalignedSliceAssumeCapacity(items);
|
||||
}
|
||||
|
||||
/// Append the slice of items to the list, asserting the capacity is already
|
||||
/// enough to store the new items. **Does not** invalidate pointers.
|
||||
/// Only call this function if calling `appendSliceAssumeCapacity` instead
|
||||
/// would be a compile error.
|
||||
/// Append the slice of items to the list.
|
||||
/// Never invalidates element pointers.
|
||||
/// This function is only needed when calling
|
||||
/// `appendSliceAssumeCapacity` instead would be a compile error due to the
|
||||
/// alignment of the `items` parameter.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void {
|
||||
const old_len = self.items.len;
|
||||
const new_len = old_len + items.len;
|
||||
@@ -348,7 +359,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
@compileError("The Writer interface is only defined for ArrayList(u8) " ++
|
||||
"but the given type is ArrayList(" ++ @typeName(T) ++ ")")
|
||||
else
|
||||
std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
|
||||
std.io.Writer(*Self, Allocator.Error, appendWrite);
|
||||
|
||||
/// Initializes a Writer which will append to the list.
|
||||
pub fn writer(self: *Self) Writer {
|
||||
@@ -357,7 +368,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// Same as `append` except it returns the number of bytes written, which is always the same
|
||||
/// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
fn appendWrite(self: *Self, m: []const u8) Allocator.Error!usize {
|
||||
try self.appendSlice(m);
|
||||
return m.len;
|
||||
@@ -365,19 +376,20 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// Append a value to the list `n` times.
|
||||
/// Allocates more memory as necessary.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
/// The function is inline so that a comptime-known `value` parameter will
|
||||
/// have a more optimal memset codegen in case it has a repeated byte pattern.
|
||||
pub inline fn appendNTimes(self: *Self, value: T, n: usize) Allocator.Error!void {
|
||||
const old_len = self.items.len;
|
||||
try self.resize(self.items.len + n);
|
||||
try self.resize(try addOrOom(old_len, n));
|
||||
@memset(self.items[old_len..self.items.len], value);
|
||||
}
|
||||
|
||||
/// Append a value to the list `n` times.
|
||||
/// Asserts the capacity is enough. **Does not** invalidate pointers.
|
||||
/// Never invalidates element pointers.
|
||||
/// The function is inline so that a comptime-known `value` parameter will
|
||||
/// have a more optimal memset codegen in case it has a repeated byte pattern.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub inline fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
|
||||
const new_len = self.items.len + n;
|
||||
assert(new_len <= self.capacity);
|
||||
@@ -385,9 +397,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
self.items.len = new_len;
|
||||
}
|
||||
|
||||
/// Adjust the list's length to `new_len`.
|
||||
/// Does not initialize added items if any.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Adjust the list length to `new_len`.
|
||||
/// Additional elements contain the value `undefined`.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn resize(self: *Self, new_len: usize) Allocator.Error!void {
|
||||
try self.ensureTotalCapacity(new_len);
|
||||
self.items.len = new_len;
|
||||
@@ -395,6 +407,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// Reduce allocated capacity to `new_len`.
|
||||
/// May invalidate element pointers.
|
||||
/// Asserts that the new length is less than or equal to the previous length.
|
||||
pub fn shrinkAndFree(self: *Self, new_len: usize) void {
|
||||
var unmanaged = self.moveToUnmanaged();
|
||||
unmanaged.shrinkAndFree(self.allocator, new_len);
|
||||
@@ -402,7 +415,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
}
|
||||
|
||||
/// Reduce length to `new_len`.
|
||||
/// Invalidates pointers for the elements `items[new_len..]`.
|
||||
/// Invalidates element pointers for the elements `items[new_len..]`.
|
||||
/// Asserts that the new length is less than or equal to the previous length.
|
||||
pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
|
||||
assert(new_len <= self.items.len);
|
||||
self.items.len = new_len;
|
||||
@@ -422,7 +436,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// If the current capacity is less than `new_capacity`, this function will
|
||||
/// modify the array so that it can hold at least `new_capacity` items.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) Allocator.Error!void {
|
||||
if (@sizeOf(T) == 0) {
|
||||
self.capacity = math.maxInt(usize);
|
||||
@@ -437,7 +451,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// If the current capacity is less than `new_capacity`, this function will
|
||||
/// modify the array so that it can hold exactly `new_capacity` items.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) Allocator.Error!void {
|
||||
if (@sizeOf(T) == 0) {
|
||||
self.capacity = math.maxInt(usize);
|
||||
@@ -464,13 +478,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
}
|
||||
|
||||
/// Modify the array so that it can hold at least `additional_count` **more** items.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn ensureUnusedCapacity(self: *Self, additional_count: usize) Allocator.Error!void {
|
||||
return self.ensureTotalCapacity(self.items.len + additional_count);
|
||||
return self.ensureTotalCapacity(try addOrOom(self.items.len, additional_count));
|
||||
}
|
||||
|
||||
/// Increases the array's length to match the full capacity that is already allocated.
|
||||
/// The new elements have `undefined` values. **Does not** invalidate pointers.
|
||||
/// The new elements have `undefined` values.
|
||||
/// Never invalidates element pointers.
|
||||
pub fn expandToCapacity(self: *Self) void {
|
||||
self.items.len = self.capacity;
|
||||
}
|
||||
@@ -478,14 +493,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Increase length by 1, returning pointer to the new item.
|
||||
/// The returned pointer becomes invalid when the list resized.
|
||||
pub fn addOne(self: *Self) Allocator.Error!*T {
|
||||
try self.ensureTotalCapacity(self.items.len + 1);
|
||||
try self.ensureUnusedCapacity(1);
|
||||
return self.addOneAssumeCapacity();
|
||||
}
|
||||
|
||||
/// Increase length by 1, returning pointer to the new item.
|
||||
/// Asserts that there is already space for the new item without allocating more.
|
||||
/// The returned pointer becomes invalid when the list is resized.
|
||||
/// **Does not** invalidate element pointers.
|
||||
/// Never invalidates element pointers.
|
||||
/// Asserts that the list can hold one additional item.
|
||||
pub fn addOneAssumeCapacity(self: *Self) *T {
|
||||
assert(self.items.len < self.capacity);
|
||||
self.items.len += 1;
|
||||
@@ -498,15 +513,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Resizes list if `self.capacity` is not large enough.
|
||||
pub fn addManyAsArray(self: *Self, comptime n: usize) Allocator.Error!*[n]T {
|
||||
const prev_len = self.items.len;
|
||||
try self.resize(self.items.len + n);
|
||||
try self.resize(try addOrOom(self.items.len, n));
|
||||
return self.items[prev_len..][0..n];
|
||||
}
|
||||
|
||||
/// Resize the array, adding `n` new elements, which have `undefined` values.
|
||||
/// The return value is an array pointing to the newly allocated elements.
|
||||
/// Asserts that there is already space for the new item without allocating more.
|
||||
/// **Does not** invalidate element pointers.
|
||||
/// Never invalidates element pointers.
|
||||
/// The returned pointer becomes invalid when the list is resized.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
|
||||
assert(self.items.len + n <= self.capacity);
|
||||
const prev_len = self.items.len;
|
||||
@@ -520,15 +535,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
/// Resizes list if `self.capacity` is not large enough.
|
||||
pub fn addManyAsSlice(self: *Self, n: usize) Allocator.Error![]T {
|
||||
const prev_len = self.items.len;
|
||||
try self.resize(self.items.len + n);
|
||||
try self.resize(try addOrOom(self.items.len, n));
|
||||
return self.items[prev_len..][0..n];
|
||||
}
|
||||
|
||||
/// Resize the array, adding `n` new elements, which have `undefined` values.
|
||||
/// The return value is a slice pointing to the newly allocated elements.
|
||||
/// Asserts that there is already space for the new item without allocating more.
|
||||
/// **Does not** invalidate element pointers.
|
||||
/// Never invalidates element pointers.
|
||||
/// The returned pointer becomes invalid when the list is resized.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn addManyAsSliceAssumeCapacity(self: *Self, n: usize) []T {
|
||||
assert(self.items.len + n <= self.capacity);
|
||||
const prev_len = self.items.len;
|
||||
@@ -537,8 +552,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
}
|
||||
|
||||
/// Remove and return the last element from the list.
|
||||
/// Asserts the list has at least one item.
|
||||
/// Invalidates pointers to the removed element.
|
||||
/// Invalidates element pointers to the removed element.
|
||||
/// Asserts that the list is not empty.
|
||||
pub fn pop(self: *Self) T {
|
||||
const val = self.items[self.items.len - 1];
|
||||
self.items.len -= 1;
|
||||
@@ -547,7 +562,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
|
||||
/// Remove and return the last element from the list, or
|
||||
/// return `null` if list is empty.
|
||||
/// Invalidates pointers to the removed element, if any.
|
||||
/// Invalidates element pointers to the removed element, if any.
|
||||
pub fn popOrNull(self: *Self) ?T {
|
||||
if (self.items.len == 0) return null;
|
||||
return self.pop();
|
||||
@@ -568,15 +583,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
return self.allocatedSlice()[self.items.len..];
|
||||
}
|
||||
|
||||
/// Return the last element from the list.
|
||||
/// Asserts the list has at least one item.
|
||||
/// Returns the last element from the list.
|
||||
/// Asserts that the list is not empty.
|
||||
pub fn getLast(self: Self) T {
|
||||
const val = self.items[self.items.len - 1];
|
||||
return val;
|
||||
}
|
||||
|
||||
/// Return the last element from the list, or
|
||||
/// return `null` if list is empty.
|
||||
/// Returns the last element from the list, or `null` if list is empty.
|
||||
pub fn getLastOrNull(self: Self) ?T {
|
||||
if (self.items.len == 0) return null;
|
||||
return self.getLast();
|
||||
@@ -585,17 +599,20 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
||||
}
|
||||
|
||||
/// An ArrayList, but the allocator is passed as a parameter to the relevant functions
|
||||
/// rather than stored in the struct itself. The same allocator **must** be used throughout
|
||||
/// rather than stored in the struct itself. The same allocator must be used throughout
|
||||
/// the entire lifetime of an ArrayListUnmanaged. Initialize directly or with
|
||||
/// `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`.
|
||||
pub fn ArrayListUnmanaged(comptime T: type) type {
|
||||
return ArrayListAlignedUnmanaged(T, null);
|
||||
}
|
||||
|
||||
/// An ArrayListAligned, but the allocator is passed as a parameter to the relevant
|
||||
/// functions rather than stored in the struct itself. The same allocator **must**
|
||||
/// be used throughout the entire lifetime of an ArrayListAlignedUnmanaged.
|
||||
/// Initialize directly or with `initCapacity`, and deinitialize with `deinit` or use `toOwnedSlice`.
|
||||
/// A contiguous, growable list of arbitrarily aligned items in memory.
|
||||
/// This is a wrapper around an array of T values aligned to `alignment`-byte
|
||||
/// addresses. If the specified alignment is `null`, then `@alignOf(T)` is used.
|
||||
///
|
||||
/// Functions that potentially allocate memory accept an `Allocator` parameter.
|
||||
/// Initialize directly or with `initCapacity`, and deinitialize with `deinit`
|
||||
/// or use `toOwnedSlice`.
|
||||
pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) type {
|
||||
if (alignment) |a| {
|
||||
if (a == @alignOf(T)) {
|
||||
@@ -604,15 +621,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
}
|
||||
return struct {
|
||||
const Self = @This();
|
||||
/// Contents of the list. Pointers to elements in this slice are
|
||||
/// **invalid after resizing operations** on the ArrayList unless the
|
||||
/// operation explicitly either: (1) states otherwise or (2) lists the
|
||||
/// invalidated pointers.
|
||||
/// Contents of the list. This field is intended to be accessed
|
||||
/// directly.
|
||||
///
|
||||
/// The allocator used determines how element pointers are
|
||||
/// invalidated, so the behavior may vary between lists. To avoid
|
||||
/// illegal behavior, take into account the above paragraph plus the
|
||||
/// explicit statements given in each method.
|
||||
/// Pointers to elements in this slice are invalidated by various
|
||||
/// functions of this ArrayList in accordance with the respective
|
||||
/// documentation. In all cases, "invalidated" means that the memory
|
||||
/// has been passed to an allocator's resize or free function.
|
||||
items: Slice = &[_]T{},
|
||||
/// How many T values this list can hold without allocating
|
||||
/// additional memory.
|
||||
@@ -635,8 +650,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// Initialize with externally-managed memory. The buffer determines the
|
||||
/// capacity, and the length is set to zero.
|
||||
/// When initialized this way, all methods that accept an Allocator
|
||||
/// argument are illegal to call.
|
||||
/// When initialized this way, all functions that accept an Allocator
|
||||
/// argument cause illegal behavior.
|
||||
pub fn initBuffer(buffer: Slice) Self {
|
||||
return .{
|
||||
.items = buffer[0..0],
|
||||
@@ -695,7 +710,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// The caller owns the returned memory. ArrayList becomes empty.
|
||||
pub fn toOwnedSliceSentinel(self: *Self, allocator: Allocator, comptime sentinel: T) Allocator.Error!SentinelSlice(sentinel) {
|
||||
try self.ensureTotalCapacityPrecise(allocator, self.items.len + 1);
|
||||
try self.ensureTotalCapacityPrecise(allocator, try addOrOom(self.items.len, 1));
|
||||
self.appendAssumeCapacity(sentinel);
|
||||
const result = try self.toOwnedSlice(allocator);
|
||||
return result[0 .. result.len - 1 :sentinel];
|
||||
@@ -708,25 +723,27 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
return cloned;
|
||||
}
|
||||
|
||||
/// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
|
||||
/// If `n` is equal to the length of the list this operation is equivalent to append.
|
||||
/// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
|
||||
/// If `i` is equal to the length of the list this operation is equivalent to append.
|
||||
/// This operation is O(N).
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
pub fn insert(self: *Self, allocator: Allocator, n: usize, item: T) Allocator.Error!void {
|
||||
const dst = try self.addManyAt(allocator, n, 1);
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn insert(self: *Self, allocator: Allocator, i: usize, item: T) Allocator.Error!void {
|
||||
const dst = try self.addManyAt(allocator, i, 1);
|
||||
dst[0] = item;
|
||||
}
|
||||
|
||||
/// Insert `item` at index `n`. Moves `list[n .. list.len]` to higher indices to make room.
|
||||
/// If `n` is equal to the length of the list this operation is equivalent to append.
|
||||
/// Insert `item` at index `i`. Moves `list[i .. list.len]` to higher indices to make room.
|
||||
/// If in` is equal to the length of the list this operation is equivalent to append.
|
||||
/// This operation is O(N).
|
||||
/// Asserts that there is enough capacity for the new item.
|
||||
pub fn insertAssumeCapacity(self: *Self, n: usize, item: T) void {
|
||||
/// Asserts that the list has capacity for one additional item.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn insertAssumeCapacity(self: *Self, i: usize, item: T) void {
|
||||
assert(self.items.len < self.capacity);
|
||||
self.items.len += 1;
|
||||
|
||||
mem.copyBackwards(T, self.items[n + 1 .. self.items.len], self.items[n .. self.items.len - 1]);
|
||||
self.items[n] = item;
|
||||
mem.copyBackwards(T, self.items[i + 1 .. self.items.len], self.items[i .. self.items.len - 1]);
|
||||
self.items[i] = item;
|
||||
}
|
||||
|
||||
/// Add `count` new elements at position `index`, which have
|
||||
@@ -736,6 +753,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// Invalidates pre-existing pointers to elements at and after `index`.
|
||||
/// Invalidates all pre-existing element pointers if capacity must be
|
||||
/// increased to accomodate the new elements.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn addManyAt(
|
||||
self: *Self,
|
||||
allocator: Allocator,
|
||||
@@ -751,9 +769,10 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// `undefined` values. Returns a slice pointing to the newly allocated
|
||||
/// elements, which becomes invalid after various `ArrayList`
|
||||
/// operations.
|
||||
/// Asserts that there is enough capacity for the new elements.
|
||||
/// Invalidates pre-existing pointers to elements at and after `index`, but
|
||||
/// does not invalidate any before that.
|
||||
/// Asserts that the list has capacity for the additional items.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T {
|
||||
const new_len = self.items.len + count;
|
||||
assert(self.capacity >= new_len);
|
||||
@@ -770,6 +789,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// Invalidates pre-existing pointers to elements at and after `index`.
|
||||
/// Invalidates all pre-existing element pointers if capacity must be
|
||||
/// increased to accomodate the new elements.
|
||||
/// Asserts that the index is in bounds or equal to the length.
|
||||
pub fn insertSlice(
|
||||
self: *Self,
|
||||
allocator: Allocator,
|
||||
@@ -787,7 +807,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// Replace range of elements `list[start..][0..len]` with `new_items`
|
||||
/// Grows list if `len < new_items.len`.
|
||||
/// Shrinks list if `len > new_items.len`
|
||||
/// Invalidates pointers if this ArrayList is resized.
|
||||
/// Invalidates element pointers if this ArrayList is resized.
|
||||
/// Asserts that the start index is in bounds or equal to the length.
|
||||
pub fn replaceRange(
|
||||
self: *Self,
|
||||
allocator: Allocator,
|
||||
@@ -801,23 +822,25 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
}
|
||||
|
||||
/// Extend the list by 1 element. Allocates more memory as necessary.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn append(self: *Self, allocator: Allocator, item: T) Allocator.Error!void {
|
||||
const new_item_ptr = try self.addOne(allocator);
|
||||
new_item_ptr.* = item;
|
||||
}
|
||||
|
||||
/// Extend the list by 1 element, but asserting `self.capacity`
|
||||
/// is sufficient to hold an additional item.
|
||||
/// Extend the list by 1 element.
|
||||
/// Never invalidates element pointers.
|
||||
/// Asserts that the list can hold one additional item.
|
||||
pub fn appendAssumeCapacity(self: *Self, item: T) void {
|
||||
const new_item_ptr = self.addOneAssumeCapacity();
|
||||
new_item_ptr.* = item;
|
||||
}
|
||||
|
||||
/// Remove the element at index `i` from the list and return its value.
|
||||
/// Asserts the array has at least one item. Invalidates pointers to
|
||||
/// last element.
|
||||
/// Invalidates pointers to the last element.
|
||||
/// This operation is O(N).
|
||||
/// Asserts that the list is not empty.
|
||||
/// Asserts that the index is in bounds.
|
||||
pub fn orderedRemove(self: *Self, i: usize) T {
|
||||
const newlen = self.items.len - 1;
|
||||
if (newlen == i) return self.pop();
|
||||
@@ -833,6 +856,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// The empty slot is filled from the end of the list.
|
||||
/// Invalidates pointers to last element.
|
||||
/// This operation is O(1).
|
||||
/// Asserts that the list is not empty.
|
||||
/// Asserts that the index is in bounds.
|
||||
pub fn swapRemove(self: *Self, i: usize) T {
|
||||
if (self.items.len - 1 == i) return self.pop();
|
||||
|
||||
@@ -843,14 +868,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// Append the slice of items to the list. Allocates more
|
||||
/// memory as necessary.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn appendSlice(self: *Self, allocator: Allocator, items: []const T) Allocator.Error!void {
|
||||
try self.ensureUnusedCapacity(allocator, items.len);
|
||||
self.appendSliceAssumeCapacity(items);
|
||||
}
|
||||
|
||||
/// Append the slice of items to the list, asserting the capacity is enough
|
||||
/// to store the new items.
|
||||
/// Append the slice of items to the list.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
|
||||
const old_len = self.items.len;
|
||||
const new_len = old_len + items.len;
|
||||
@@ -862,15 +887,16 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// Append the slice of items to the list. Allocates more
|
||||
/// memory as necessary. Only call this function if a call to `appendSlice` instead would
|
||||
/// be a compile error.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn appendUnalignedSlice(self: *Self, allocator: Allocator, items: []align(1) const T) Allocator.Error!void {
|
||||
try self.ensureUnusedCapacity(allocator, items.len);
|
||||
self.appendUnalignedSliceAssumeCapacity(items);
|
||||
}
|
||||
|
||||
/// Append an unaligned slice of items to the list, asserting the capacity is enough
|
||||
/// to store the new items. Only call this function if a call to `appendSliceAssumeCapacity`
|
||||
/// Append an unaligned slice of items to the list.
|
||||
/// Only call this function if a call to `appendSliceAssumeCapacity`
|
||||
/// instead would be a compile error.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn appendUnalignedSliceAssumeCapacity(self: *Self, items: []align(1) const T) void {
|
||||
const old_len = self.items.len;
|
||||
const new_len = old_len + items.len;
|
||||
@@ -888,7 +914,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
@compileError("The Writer interface is only defined for ArrayList(u8) " ++
|
||||
"but the given type is ArrayList(" ++ @typeName(T) ++ ")")
|
||||
else
|
||||
std.io.Writer(WriterContext, error{OutOfMemory}, appendWrite);
|
||||
std.io.Writer(WriterContext, Allocator.Error, appendWrite);
|
||||
|
||||
/// Initializes a Writer which will append to the list.
|
||||
pub fn writer(self: *Self, allocator: Allocator) Writer {
|
||||
@@ -897,7 +923,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// Same as `append` except it returns the number of bytes written, which is always the same
|
||||
/// as `m.len`. The purpose of this function existing is to match `std.io.Writer` API.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
fn appendWrite(context: WriterContext, m: []const u8) Allocator.Error!usize {
|
||||
try context.self.appendSlice(context.allocator, m);
|
||||
return m.len;
|
||||
@@ -905,20 +931,20 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// Append a value to the list `n` times.
|
||||
/// Allocates more memory as necessary.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
/// The function is inline so that a comptime-known `value` parameter will
|
||||
/// have a more optimal memset codegen in case it has a repeated byte pattern.
|
||||
pub inline fn appendNTimes(self: *Self, allocator: Allocator, value: T, n: usize) Allocator.Error!void {
|
||||
const old_len = self.items.len;
|
||||
try self.resize(allocator, self.items.len + n);
|
||||
try self.resize(allocator, try addOrOom(old_len, n));
|
||||
@memset(self.items[old_len..self.items.len], value);
|
||||
}
|
||||
|
||||
/// Append a value to the list `n` times.
|
||||
/// **Does not** invalidate pointers.
|
||||
/// Asserts the capacity is enough.
|
||||
/// Never invalidates element pointers.
|
||||
/// The function is inline so that a comptime-known `value` parameter will
|
||||
/// have a more optimal memset codegen in case it has a repeated byte pattern.
|
||||
/// have better memset codegen in case it has a repeated byte pattern.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub inline fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
|
||||
const new_len = self.items.len + n;
|
||||
assert(new_len <= self.capacity);
|
||||
@@ -926,9 +952,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
self.items.len = new_len;
|
||||
}
|
||||
|
||||
/// Adjust the list's length to `new_len`.
|
||||
/// Does not initialize added items, if any.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Adjust the list length to `new_len`.
|
||||
/// Additional elements contain the value `undefined`.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn resize(self: *Self, allocator: Allocator, new_len: usize) Allocator.Error!void {
|
||||
try self.ensureTotalCapacity(allocator, new_len);
|
||||
self.items.len = new_len;
|
||||
@@ -936,6 +962,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// Reduce allocated capacity to `new_len`.
|
||||
/// May invalidate element pointers.
|
||||
/// Asserts that the new length is less than or equal to the previous length.
|
||||
pub fn shrinkAndFree(self: *Self, allocator: Allocator, new_len: usize) void {
|
||||
assert(new_len <= self.items.len);
|
||||
|
||||
@@ -968,6 +995,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// Reduce length to `new_len`.
|
||||
/// Invalidates pointers to elements `items[new_len..]`.
|
||||
/// Keeps capacity the same.
|
||||
/// Asserts that the new length is less than or equal to the previous length.
|
||||
pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
|
||||
assert(new_len <= self.items.len);
|
||||
self.items.len = new_len;
|
||||
@@ -987,7 +1015,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// If the current capacity is less than `new_capacity`, this function will
|
||||
/// modify the array so that it can hold at least `new_capacity` items.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void {
|
||||
if (self.capacity >= new_capacity) return;
|
||||
|
||||
@@ -997,7 +1025,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
|
||||
/// If the current capacity is less than `new_capacity`, this function will
|
||||
/// modify the array so that it can hold exactly `new_capacity` items.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn ensureTotalCapacityPrecise(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void {
|
||||
if (@sizeOf(T) == 0) {
|
||||
self.capacity = math.maxInt(usize);
|
||||
@@ -1024,34 +1052,34 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
}
|
||||
|
||||
/// Modify the array so that it can hold at least `additional_count` **more** items.
|
||||
/// Invalidates pointers if additional memory is needed.
|
||||
/// Invalidates element pointers if additional memory is needed.
|
||||
pub fn ensureUnusedCapacity(
|
||||
self: *Self,
|
||||
allocator: Allocator,
|
||||
additional_count: usize,
|
||||
) Allocator.Error!void {
|
||||
return self.ensureTotalCapacity(allocator, self.items.len + additional_count);
|
||||
return self.ensureTotalCapacity(allocator, try addOrOom(self.items.len, additional_count));
|
||||
}
|
||||
|
||||
/// Increases the array's length to match the full capacity that is already allocated.
|
||||
/// The new elements have `undefined` values.
|
||||
/// **Does not** invalidate pointers.
|
||||
/// Never invalidates element pointers.
|
||||
pub fn expandToCapacity(self: *Self) void {
|
||||
self.items.len = self.capacity;
|
||||
}
|
||||
|
||||
/// Increase length by 1, returning pointer to the new item.
|
||||
/// The returned pointer becomes invalid when the list resized.
|
||||
/// The returned element pointer becomes invalid when the list is resized.
|
||||
pub fn addOne(self: *Self, allocator: Allocator) Allocator.Error!*T {
|
||||
const newlen = self.items.len + 1;
|
||||
const newlen = try addOrOom(self.items.len, 1);
|
||||
try self.ensureTotalCapacity(allocator, newlen);
|
||||
return self.addOneAssumeCapacity();
|
||||
}
|
||||
|
||||
/// Increase length by 1, returning pointer to the new item.
|
||||
/// Asserts that there is already space for the new item without allocating more.
|
||||
/// **Does not** invalidate pointers.
|
||||
/// The returned pointer becomes invalid when the list resized.
|
||||
/// Never invalidates element pointers.
|
||||
/// The returned element pointer becomes invalid when the list is resized.
|
||||
/// Asserts that the list can hold one additional item.
|
||||
pub fn addOneAssumeCapacity(self: *Self) *T {
|
||||
assert(self.items.len < self.capacity);
|
||||
|
||||
@@ -1064,15 +1092,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// The returned pointer becomes invalid when the list is resized.
|
||||
pub fn addManyAsArray(self: *Self, allocator: Allocator, comptime n: usize) Allocator.Error!*[n]T {
|
||||
const prev_len = self.items.len;
|
||||
try self.resize(allocator, self.items.len + n);
|
||||
try self.resize(allocator, try addOrOom(self.items.len, n));
|
||||
return self.items[prev_len..][0..n];
|
||||
}
|
||||
|
||||
/// Resize the array, adding `n` new elements, which have `undefined` values.
|
||||
/// The return value is an array pointing to the newly allocated elements.
|
||||
/// Asserts that there is already space for the new item without allocating more.
|
||||
/// **Does not** invalidate pointers.
|
||||
/// Never invalidates element pointers.
|
||||
/// The returned pointer becomes invalid when the list is resized.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn addManyAsArrayAssumeCapacity(self: *Self, comptime n: usize) *[n]T {
|
||||
assert(self.items.len + n <= self.capacity);
|
||||
const prev_len = self.items.len;
|
||||
@@ -1086,15 +1114,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
/// Resizes list if `self.capacity` is not large enough.
|
||||
pub fn addManyAsSlice(self: *Self, allocator: Allocator, n: usize) Allocator.Error![]T {
|
||||
const prev_len = self.items.len;
|
||||
try self.resize(allocator, self.items.len + n);
|
||||
try self.resize(allocator, try addOrOom(self.items.len, n));
|
||||
return self.items[prev_len..][0..n];
|
||||
}
|
||||
|
||||
/// Resize the array, adding `n` new elements, which have `undefined` values.
|
||||
/// The return value is a slice pointing to the newly allocated elements.
|
||||
/// Asserts that there is already space for the new item without allocating more.
|
||||
/// **Does not** invalidate element pointers.
|
||||
/// Never invalidates element pointers.
|
||||
/// The returned pointer becomes invalid when the list is resized.
|
||||
/// Asserts that the list can hold the additional items.
|
||||
pub fn addManyAsSliceAssumeCapacity(self: *Self, n: usize) []T {
|
||||
assert(self.items.len + n <= self.capacity);
|
||||
const prev_len = self.items.len;
|
||||
@@ -1103,8 +1131,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
}
|
||||
|
||||
/// Remove and return the last element from the list.
|
||||
/// Asserts the list has at least one item.
|
||||
/// Invalidates pointers to last element.
|
||||
/// Asserts that the list is not empty.
|
||||
pub fn pop(self: *Self) T {
|
||||
const val = self.items[self.items.len - 1];
|
||||
self.items.len -= 1;
|
||||
@@ -1134,7 +1162,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
||||
}
|
||||
|
||||
/// Return the last element from the list.
|
||||
/// Asserts the list has at least one item.
|
||||
/// Asserts that the list is not empty.
|
||||
pub fn getLast(self: Self) T {
|
||||
const val = self.items[self.items.len - 1];
|
||||
return val;
|
||||
@@ -1160,6 +1188,13 @@ fn growCapacity(current: usize, minimum: usize) usize {
|
||||
}
|
||||
}
|
||||
|
||||
/// Integer addition returning `error.OutOfMemory` on overflow.
|
||||
fn addOrOom(a: usize, b: usize) error{OutOfMemory}!usize {
|
||||
const result, const overflow = @addWithOverflow(a, b);
|
||||
if (overflow != 0) return error.OutOfMemory;
|
||||
return result;
|
||||
}
|
||||
|
||||
test "std.ArrayList/ArrayListUnmanaged.init" {
|
||||
{
|
||||
var list = ArrayList(i32).init(testing.allocator);
|
||||
@@ -1952,3 +1987,51 @@ test "std.ArrayList(u32).getLastOrNull()" {
|
||||
const const_list = list;
|
||||
try testing.expectEqual(const_list.getLastOrNull().?, 2);
|
||||
}
|
||||
|
||||
test "return OutOfMemory when capacity would exceed maximum usize integer value" {
|
||||
const a = testing.allocator;
|
||||
const new_item: u32 = 42;
|
||||
|
||||
{
|
||||
var list: ArrayListUnmanaged(u32) = .{
|
||||
.items = undefined,
|
||||
.capacity = math.maxInt(usize),
|
||||
};
|
||||
list.items.len = math.maxInt(usize);
|
||||
|
||||
try testing.expectError(error.OutOfMemory, list.append(a, new_item));
|
||||
try testing.expectError(error.OutOfMemory, list.appendSlice(a, &.{new_item}));
|
||||
try testing.expectError(error.OutOfMemory, list.appendNTimes(a, new_item, 1));
|
||||
try testing.expectError(error.OutOfMemory, list.appendUnalignedSlice(a, &.{new_item}));
|
||||
try testing.expectError(error.OutOfMemory, list.addOne(a));
|
||||
try testing.expectError(error.OutOfMemory, list.addManyAt(a, 0, 1));
|
||||
try testing.expectError(error.OutOfMemory, list.addManyAsArray(a, 1));
|
||||
try testing.expectError(error.OutOfMemory, list.addManyAsSlice(a, 1));
|
||||
try testing.expectError(error.OutOfMemory, list.insert(a, 0, new_item));
|
||||
try testing.expectError(error.OutOfMemory, list.insertSlice(a, 0, &.{new_item}));
|
||||
try testing.expectError(error.OutOfMemory, list.toOwnedSliceSentinel(a, 0));
|
||||
try testing.expectError(error.OutOfMemory, list.ensureUnusedCapacity(a, 1));
|
||||
}
|
||||
|
||||
{
|
||||
var list: ArrayList(u32) = .{
|
||||
.items = undefined,
|
||||
.capacity = math.maxInt(usize),
|
||||
.allocator = a,
|
||||
};
|
||||
list.items.len = math.maxInt(usize);
|
||||
|
||||
try testing.expectError(error.OutOfMemory, list.append(new_item));
|
||||
try testing.expectError(error.OutOfMemory, list.appendSlice(&.{new_item}));
|
||||
try testing.expectError(error.OutOfMemory, list.appendNTimes(new_item, 1));
|
||||
try testing.expectError(error.OutOfMemory, list.appendUnalignedSlice(&.{new_item}));
|
||||
try testing.expectError(error.OutOfMemory, list.addOne());
|
||||
try testing.expectError(error.OutOfMemory, list.addManyAt(0, 1));
|
||||
try testing.expectError(error.OutOfMemory, list.addManyAsArray(1));
|
||||
try testing.expectError(error.OutOfMemory, list.addManyAsSlice(1));
|
||||
try testing.expectError(error.OutOfMemory, list.insert(0, new_item));
|
||||
try testing.expectError(error.OutOfMemory, list.insertSlice(0, &.{new_item}));
|
||||
try testing.expectError(error.OutOfMemory, list.toOwnedSliceSentinel(0));
|
||||
try testing.expectError(error.OutOfMemory, list.ensureUnusedCapacity(1));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user