commit 86a9a9048e28efc8b49d42db12db123822c3450b (tree)
parent 0cbaaa5eb9434eb9484bae95949a4086eb472c6a
Author: Rue04 <iokg04@gmail.com>
Date: Sat, 3 Jan 2026 20:32:07 +0100
`std.MultiArrayList`: add `*Bounded` variants and `initCapacity`
Because I accidentially squished two commit and can't figure out how to separate them
again, this also
- standardizes some doc-comments
- makes a slight change to `std.ArrayList`'s `initCapacity`'s doc-comment
Diffstat:
2 files changed, 94 insertions(+), 38 deletions(-)
diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig
@@ -599,11 +599,10 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
return if (alignment) |a| ([:s]align(a.toByteUnits()) T) else [:s]T;
}
- /// Initialize with capacity to hold `num` elements.
- /// The resulting capacity will equal `num` exactly.
- /// Deinitialize with `deinit` or use `toOwnedSlice`.
+ /// Initialize with capacity to hold exactly `num` elements.
+ /// Deinitialize with `deinit` or `toOwnedSlice`.
pub fn initCapacity(gpa: Allocator, num: usize) Allocator.Error!Self {
- var self = Self{};
+ var self: Self = .empty;
try self.ensureTotalCapacityPrecise(gpa, num);
return self;
}
diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig
@@ -29,6 +29,14 @@ pub fn MultiArrayList(comptime T: type) type {
.capacity = 0,
};
+ /// Initialize with capacity to hold exactly `num` elements.
+ /// Deinitialize with `deinit` or `toOwnedSlice`.
+ pub fn initCapacity(gpa: Allocator, num: usize) Allocator.Error!Self {
+ var self: Self = .empty;
+ try self.setCapacity(gpa, num);
+ return self;
+ }
+
const Elem = switch (@typeInfo(T)) {
.@"struct" => T,
.@"union" => |u| struct {
@@ -253,31 +261,45 @@ pub fn MultiArrayList(comptime T: type) type {
return self.slice().get(index);
}
- /// Extend the list by 1 element. Allocates more memory as necessary.
- pub fn append(self: *Self, gpa: Allocator, elem: T) !void {
+ /// Extend the list by 1 element.
+ ///
+ /// Allocates more memory as necessary.
+ pub fn append(self: *Self, gpa: Allocator, elem: T) Allocator.Error!void {
try self.ensureUnusedCapacity(gpa, 1);
self.appendAssumeCapacity(elem);
}
- /// Extend the list by 1 element, but asserting `self.capacity`
- /// is sufficient to hold an additional item.
+ /// Extend the list by 1 element.
+ ///
+ /// Asserts that capacity is sufficient to hold an additional item.
pub fn appendAssumeCapacity(self: *Self, elem: T) void {
assert(self.len < self.capacity);
self.len += 1;
self.set(self.len - 1, elem);
}
+ /// Extend the list by 1 element.
+ ///
+ /// If capacity is not sufficient to hold an additional
+ /// item, returns `error.OutOfMemory`.
+ pub fn appendBounded(self: *Self, elem: T) error{OutOfMemory}!void {
+ if (self.capacity - self.len < 1) return error.OutOfMemory;
+ return appendAssumeCapacity(self, elem);
+ }
+
/// Extend the list by 1 element, returning the newly reserved
/// index with uninitialized data.
- /// Allocates more memory as necesasry.
+ ///
+ /// Allocates more memory as necessary.
pub fn addOne(self: *Self, gpa: Allocator) Allocator.Error!usize {
try self.ensureUnusedCapacity(gpa, 1);
return self.addOneAssumeCapacity();
}
- /// Extend the list by 1 element, asserting `self.capacity`
- /// is sufficient to hold an additional item. Returns the
- /// newly reserved index with uninitialized data.
+ /// Extend the list by 1 element, returning the newly reserved
+ /// index with uninitialized data.
+ ///
+ /// Asserts that capacity is sufficient to hold an additional item.
pub fn addOneAssumeCapacity(self: *Self) usize {
assert(self.len < self.capacity);
const index = self.len;
@@ -285,6 +307,16 @@ pub fn MultiArrayList(comptime T: type) type {
return index;
}
+ /// Extend the list by 1 element, returning the newly reserved
+ /// index with uninitialized data.
+ ///
+ /// If capacity is not sufficient to hold an additional
+ /// item, returns `error.OutOfMemory`.
+ pub fn addOneBounded(self: *Self) error{OutOfMemory}!usize {
+ if (self.capacity - self.len < 1) return error.OutOfMemory;
+ return addOneAssumeCapacity(self);
+ }
+
/// Remove and return the last element from the list, or return `null` if list is empty.
/// Invalidates pointers to fields of the removed element.
pub fn pop(self: *Self) ?T {
@@ -294,19 +326,21 @@ pub fn MultiArrayList(comptime T: type) type {
return val;
}
- /// Inserts an item into an ordered list. Shifts all elements
+ /// Inserts an item into the list. Shifts all elements
/// after and including the specified index back by one and
- /// sets the given index to the specified element. May reallocate
- /// and invalidate iterators.
+ /// sets the given index to the specified element.
+ ///
+ /// Allocates more memory as necessary.
pub fn insert(self: *Self, gpa: Allocator, index: usize, elem: T) !void {
try self.ensureUnusedCapacity(gpa, 1);
self.insertAssumeCapacity(index, elem);
}
- /// Inserts an item into an ordered list which has room for it.
- /// Shifts all elements after and including the specified index
- /// back by one and sets the given index to the specified element.
- /// Will not reallocate the array, does not invalidate iterators.
+ /// Inserts an item into the list. Shifts all elements
+ /// after and including the specified index back by one and
+ /// sets the given index to the specified element.
+ ///
+ /// Asserts that capacity is sufficient to hold an additional item.
pub fn insertAssumeCapacity(self: *Self, index: usize, elem: T) void {
assert(self.len < self.capacity);
assert(index <= self.len);
@@ -327,8 +361,19 @@ pub fn MultiArrayList(comptime T: type) type {
}
}
+ /// Inserts an item into the list. Shifts all elements
+ /// after and including the specified index back by one and
+ /// sets the given index to the specified element.
+ ///
+ /// If capacity is not sufficient to hold an additional
+ /// item, returns `error.OutOfMemory`.
+ pub fn insertBounded(self: *Self, index: usize, elem: T) error{OutOfMemory}!void {
+ if (self.capacity - self.len < 1) return error.OutOfMemory;
+ return insertAssumeCapacity(self, index, elem);
+ }
+
/// Remove the specified item from the list, swapping the last
- /// item in the list into its position. Fast, but does not
+ /// item in the list into its position. Fast, but does not
/// retain list ordering.
pub fn swapRemove(self: *Self, index: usize) void {
const slices = self.slice();
@@ -393,7 +438,7 @@ pub fn MultiArrayList(comptime T: type) type {
/// Adjust the list's length to `new_len`.
/// Does not initialize added items, if any.
- pub fn resize(self: *Self, gpa: Allocator, new_len: usize) !void {
+ pub fn resize(self: *Self, gpa: Allocator, new_len: usize) Allocator.Error!void {
try self.ensureTotalCapacity(gpa, new_len);
self.len = new_len;
}
@@ -479,14 +524,14 @@ pub fn MultiArrayList(comptime T: type) type {
/// Modify the array so that it can hold at least `additional_count` **more** items.
/// Invalidates pointers if additional memory is needed.
- pub fn ensureUnusedCapacity(self: *Self, gpa: Allocator, additional_count: usize) !void {
+ pub fn ensureUnusedCapacity(self: *Self, gpa: Allocator, additional_count: usize) Allocator.Error!void {
return self.ensureTotalCapacity(gpa, self.len + additional_count);
}
/// Modify the array so that it can hold exactly `new_capacity` items.
/// Invalidates pointers if additional memory is needed.
/// `new_capacity` must be greater or equal to `len`.
- pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) !void {
+ pub fn setCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
assert(new_capacity >= self.len);
const new_bytes = try gpa.alignedAlloc(u8, .of(Elem), capacityInBytes(new_capacity));
if (self.len == 0) {
@@ -514,7 +559,7 @@ pub fn MultiArrayList(comptime T: type) type {
/// Create a copy of this list with a new backing store,
/// using the specified allocator.
- pub fn clone(self: Self, gpa: Allocator) !Self {
+ pub fn clone(self: Self, gpa: Allocator) Allocator.Error!Self {
var result = Self{};
errdefer result.deinit(gpa);
try result.ensureTotalCapacity(gpa, self.len);
@@ -654,7 +699,7 @@ test "basic usage" {
c: u8,
};
- var list = MultiArrayList(Foo){};
+ var list: MultiArrayList(Foo) = .empty;
defer list.deinit(ally);
try testing.expectEqual(@as(usize, 0), list.items(.a).len);
@@ -667,7 +712,7 @@ test "basic usage" {
.c = 'a',
});
- list.appendAssumeCapacity(.{
+ try list.appendBounded(.{
.a = 2,
.b = "zigzag",
.c = 'b',
@@ -725,6 +770,8 @@ test "basic usage" {
try testing.expectEqualStrings("zigzag", list.items(.b)[1]);
try testing.expectEqualStrings("fizzbuzz", list.items(.b)[2]);
+ try testing.expectError(error.OutOfMemory, list.addOneBounded());
+
list.set(try list.addOne(ally), .{
.a = 4,
.b = "xnopyt",
@@ -749,10 +796,10 @@ test "basic usage" {
// function used the @reduce code path.
test "regression test for @reduce bug" {
const ally = testing.allocator;
- var list = MultiArrayList(struct {
+ var list: MultiArrayList(struct {
tag: std.zig.Token.Tag,
start: u32,
- }){};
+ }) = .empty;
defer list.deinit(ally);
try list.ensureTotalCapacity(ally, 20);
@@ -832,7 +879,7 @@ test "ensure capacity on empty list" {
b: u8,
};
- var list = MultiArrayList(Foo){};
+ var list: MultiArrayList(Foo) = .empty;
defer list.deinit(ally);
try list.ensureTotalCapacity(ally, 2);
@@ -867,15 +914,25 @@ test "insert elements" {
b: u32,
};
- var list = MultiArrayList(Foo){};
+ var list = try MultiArrayList(Foo).initCapacity(ally, 2);
defer list.deinit(ally);
- try list.insert(ally, 0, .{ .a = 1, .b = 2 });
- try list.ensureUnusedCapacity(ally, 1);
+ try list.insertBounded(0, .{ .a = 1, .b = 2 });
list.insertAssumeCapacity(1, .{ .a = 2, .b = 3 });
+ try list.insert(ally, 0, .{ .a = 3, .b = 4 });
+
+ try testing.expectEqualSlices(u8, &[_]u8{ 3, 1, 2 }, list.items(.a));
+ try testing.expectEqualSlices(u32, &[_]u32{ 4, 2, 3 }, list.items(.b));
+}
+
+test "initCapacity" {
+ const gpa = testing.allocator;
+
+ var list = try MultiArrayList(struct { a: u8, b: u32 }).initCapacity(gpa, 404);
+ defer list.deinit(gpa);
- try testing.expectEqualSlices(u8, &[_]u8{ 1, 2 }, list.items(.a));
- try testing.expectEqualSlices(u32, &[_]u32{ 2, 3 }, list.items(.b));
+ try testing.expectEqual(0, list.len);
+ try testing.expectEqual(404, list.capacity);
}
test "union" {
@@ -886,7 +943,7 @@ test "union" {
b: []const u8,
};
- var list = MultiArrayList(Foo){};
+ var list: MultiArrayList(Foo) = .empty;
defer list.deinit(ally);
try testing.expectEqual(@as(usize, 0), list.items(.tags).len);
@@ -934,7 +991,7 @@ test "union" {
}
test "sorting a span" {
- var list: MultiArrayList(struct { score: u32, chr: u8 }) = .{};
+ var list: MultiArrayList(struct { score: u32, chr: u8 }) = .empty;
defer list.deinit(testing.allocator);
try list.ensureTotalCapacity(testing.allocator, 42);
@@ -981,7 +1038,7 @@ test "0 sized struct field" {
b: f32,
};
- var list = MultiArrayList(Foo){};
+ var list: MultiArrayList(Foo) = .empty;
defer list.deinit(ally);
try testing.expectEqualSlices(u0, &[_]u0{}, list.items(.a));
@@ -1007,7 +1064,7 @@ test "0 sized struct" {
a: u0,
};
- var list = MultiArrayList(Foo){};
+ var list: MultiArrayList(Foo) = .empty;
defer list.deinit(ally);
try testing.expectEqualSlices(u0, &[_]u0{}, list.items(.a));