Commit 2588451967

Zach Banks <zjbanks@gmail.com>
2021-04-23 20:24:58
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 551971b
Changed files (1)
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);
+}