From fd162881e2b39b63cc52bb0198d929ddf3c2449e Mon Sep 17 00:00:00 2001 From: Zach Banks Date: Fri, 23 Apr 2021 14:24:58 -0400 Subject: [PATCH] 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. --- lib/std/array_list.zig | 77 ++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 8b46ec21459e..1e74d34922a8 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -65,16 +65,23 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { pub fn initCapacity(allocator: *Allocator, num: usize) !Self { var self = Self.init(allocator); - const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least); - self.items.ptr = new_memory.ptr; - self.capacity = new_memory.len; + if (@sizeOf(T) > 0) { + const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least); + self.items.ptr = new_memory.ptr; + self.capacity = new_memory.len; + } else { + // If `T` is a zero-sized type, then we do not need to allocate memory. + self.capacity = std.math.maxInt(usize); + } return self; } /// Release all allocated memory. pub fn deinit(self: Self) void { - self.allocator.free(self.allocatedSlice()); + if (@sizeOf(T) > 0) { + self.allocator.free(self.allocatedSlice()); + } } pub const span = @compileError("deprecated: use `items` field directly"); @@ -279,13 +286,17 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { pub fn shrinkAndFree(self: *Self, new_len: usize) void { assert(new_len <= self.items.len); - self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) { - error.OutOfMemory => { // no problem, capacity is still correct then. - self.items.len = new_len; - return; - }, - }; - self.capacity = new_len; + if (@sizeOf(T) > 0) { + self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) { + error.OutOfMemory => { // no problem, capacity is still correct then. + self.items.len = new_len; + return; + }, + }; + self.capacity = new_len; + } else { + self.items.len = new_len; + } } /// Reduce length to `new_len`. @@ -313,18 +324,22 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { /// Modify the array so that it can hold at least `new_capacity` items. /// Invalidates pointers if additional memory is needed. pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void { - var better_capacity = self.capacity; - if (better_capacity >= new_capacity) return; + if (@sizeOf(T) > 0) { + var better_capacity = self.capacity; + if (better_capacity >= new_capacity) return; - while (true) { - better_capacity += better_capacity / 2 + 8; - if (better_capacity >= new_capacity) break; - } + while (true) { + better_capacity += better_capacity / 2 + 8; + if (better_capacity >= new_capacity) break; + } - // TODO This can be optimized to avoid needlessly copying undefined memory. - const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity); - self.items.ptr = new_memory.ptr; - self.capacity = new_memory.len; + // TODO This can be optimized to avoid needlessly copying undefined memory. + const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity); + self.items.ptr = new_memory.ptr; + self.capacity = new_memory.len; + } else { + self.capacity = std.math.maxInt(usize); + } } /// Modify the array so that it can hold at least `additional_count` **more** items. @@ -1299,3 +1314,23 @@ test "ArrayListAligned/ArrayListAlignedUnmanaged accepts unaligned slices" { try testing.expectEqualSlices(u8, list.items, &.{ 0, 8, 9, 6, 7, 2, 3 }); } } + +test "std.ArrayList(u0)" { + // An ArrayList on zero-sized types should not need to allocate + const a = &testing.FailingAllocator.init(testing.allocator, 0).allocator; + + var list = ArrayList(u0).init(a); + defer list.deinit(); + + try list.append(0); + try list.append(0); + try list.append(0); + try testing.expectEqual(list.items.len, 3); + + var count: usize = 0; + for (list.items) |x| { + try testing.expectEqual(x, 0); + count += 1; + } + try testing.expectEqual(count, 3); +}