Skip to content

Commit a857a9b

Browse files
committed
std/ArrayList: Allow ArrayList(u0) to be created
Enable creating ArrayList with zero-sized types. This type still tracks length, but does not allocate additional memory.
1 parent 2c9ed6d commit a857a9b

File tree

1 file changed

+56
-21
lines changed

1 file changed

+56
-21
lines changed

lib/std/array_list.zig

+56-21
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,23 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
6666
pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
6767
var self = Self.init(allocator);
6868

69-
const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);
70-
self.items.ptr = new_memory.ptr;
71-
self.capacity = new_memory.len;
69+
if (@sizeOf(T) > 0) {
70+
const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);
71+
self.items.ptr = new_memory.ptr;
72+
self.capacity = new_memory.len;
73+
} else {
74+
// If `T` is a zero-sized type, then we do not need to allocate memory.
75+
self.capacity = std.math.maxInt(usize);
76+
}
7277

7378
return self;
7479
}
7580

7681
/// Release all allocated memory.
7782
pub fn deinit(self: Self) void {
78-
self.allocator.free(self.allocatedSlice());
83+
if (@sizeOf(T) > 0) {
84+
self.allocator.free(self.allocatedSlice());
85+
}
7986
}
8087

8188
pub const span = @compileError("deprecated: use `items` field directly");
@@ -279,13 +286,17 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
279286
pub fn shrinkAndFree(self: *Self, new_len: usize) void {
280287
assert(new_len <= self.items.len);
281288

282-
self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {
283-
error.OutOfMemory => { // no problem, capacity is still correct then.
284-
self.items.len = new_len;
285-
return;
286-
},
287-
};
288-
self.capacity = new_len;
289+
if (@sizeOf(T) > 0) {
290+
self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {
291+
error.OutOfMemory => { // no problem, capacity is still correct then.
292+
self.items.len = new_len;
293+
return;
294+
},
295+
};
296+
self.capacity = new_len;
297+
} else {
298+
self.items.len = new_len;
299+
}
289300
}
290301

291302
/// Reduce length to `new_len`.
@@ -298,18 +309,22 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
298309
/// Modify the array so that it can hold at least `new_capacity` items.
299310
/// Invalidates pointers if additional memory is needed.
300311
pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
301-
var better_capacity = self.capacity;
302-
if (better_capacity >= new_capacity) return;
312+
if (@sizeOf(T) > 0) {
313+
var better_capacity = self.capacity;
314+
if (better_capacity >= new_capacity) return;
303315

304-
while (true) {
305-
better_capacity += better_capacity / 2 + 8;
306-
if (better_capacity >= new_capacity) break;
307-
}
316+
while (true) {
317+
better_capacity += better_capacity / 2 + 8;
318+
if (better_capacity >= new_capacity) break;
319+
}
308320

309-
// TODO This can be optimized to avoid needlessly copying undefined memory.
310-
const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
311-
self.items.ptr = new_memory.ptr;
312-
self.capacity = new_memory.len;
321+
// TODO This can be optimized to avoid needlessly copying undefined memory.
322+
const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
323+
self.items.ptr = new_memory.ptr;
324+
self.capacity = new_memory.len;
325+
} else {
326+
self.capacity = std.math.maxInt(usize);
327+
}
313328
}
314329

315330
/// Increases the array's length to match the full capacity that is already allocated.
@@ -1226,3 +1241,23 @@ test "std.ArrayList/ArrayListUnmanaged.toOwnedSliceSentinel" {
12261241
testing.expectEqualStrings(result, mem.spanZ(result.ptr));
12271242
}
12281243
}
1244+
1245+
test "std.ArrayList(u0)" {
1246+
// An ArrayList on zero-sized types should not need to allocate
1247+
const a = &testing.FailingAllocator.init(testing.allocator, 0).allocator;
1248+
1249+
var list = ArrayList(u0).init(a);
1250+
defer list.deinit();
1251+
1252+
try list.append(0);
1253+
try list.append(0);
1254+
try list.append(0);
1255+
testing.expectEqual(list.items.len, 3);
1256+
1257+
var count: usize = 0;
1258+
for (list.items) |x| {
1259+
testing.expectEqual(x, 0);
1260+
count += 1;
1261+
}
1262+
testing.expectEqual(count, 3);
1263+
}

0 commit comments

Comments
 (0)