authorgravatar for zjbanks@gmail.comZach Banks <zjbanks@gmail.com> 2021-04-23 14:24:58-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-31 13:41:05-07:00
log258845196702f458d00d7e974f05437100c8fc8f
tree0310107261cea2ec688185c4f50e0e8504ae4331
parent551971b5c373c98b3cf111492bc8a2048e0e7130

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 files changed, 56 insertions(+), 21 deletions(-)

lib/std/array_list.zig+56-21
...@@ -65,16 +65,23 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -65,16 +65,23 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
65 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {65 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
66 var self = Self.init(allocator);66 var self = Self.init(allocator);
6767
68 const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);68 if (@sizeOf(T) > 0) {
69 self.items.ptr = new_memory.ptr;69 const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);
70 self.capacity = new_memory.len;70 self.items.ptr = new_memory.ptr;
71 self.capacity = new_memory.len;
72 } else {
73 // If `T` is a zero-sized type, then we do not need to allocate memory.
74 self.capacity = std.math.maxInt(usize);
75 }
7176
72 return self;77 return self;
73 }78 }
7479
75 /// Release all allocated memory.80 /// Release all allocated memory.
76 pub fn deinit(self: Self) void {81 pub fn deinit(self: Self) void {
77 self.allocator.free(self.allocatedSlice());82 if (@sizeOf(T) > 0) {
83 self.allocator.free(self.allocatedSlice());
84 }
78 }85 }
7986
80 pub const span = @compileError("deprecated: use `items` field directly");87 pub const span = @compileError("deprecated: use `items` field directly");
...@@ -279,13 +286,17 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -279,13 +286,17 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
279 pub fn shrinkAndFree(self: *Self, new_len: usize) void {286 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
280 assert(new_len <= self.items.len);287 assert(new_len <= self.items.len);
281288
282 self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {289 if (@sizeOf(T) > 0) {
283 error.OutOfMemory => { // no problem, capacity is still correct then.290 self.items = self.allocator.realloc(self.allocatedSlice(), new_len) catch |e| switch (e) {
284 self.items.len = new_len;291 error.OutOfMemory => { // no problem, capacity is still correct then.
285 return;292 self.items.len = new_len;
286 },293 return;
287 };294 },
288 self.capacity = new_len;295 };
296 self.capacity = new_len;
297 } else {
298 self.items.len = new_len;
299 }
289 }300 }
290301
291 /// Reduce length to `new_len`.302 /// Reduce length to `new_len`.
...@@ -313,18 +324,22 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -313,18 +324,22 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
313 /// Modify the array so that it can hold at least `new_capacity` items.324 /// Modify the array so that it can hold at least `new_capacity` items.
314 /// Invalidates pointers if additional memory is needed.325 /// Invalidates pointers if additional memory is needed.
315 pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void {326 pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void {
316 var better_capacity = self.capacity;327 if (@sizeOf(T) > 0) {
317 if (better_capacity >= new_capacity) return;328 var better_capacity = self.capacity;
329 if (better_capacity >= new_capacity) return;
318330
319 while (true) {331 while (true) {
320 better_capacity += better_capacity / 2 + 8;332 better_capacity += better_capacity / 2 + 8;
321 if (better_capacity >= new_capacity) break;333 if (better_capacity >= new_capacity) break;
322 }334 }
323335
324 // TODO This can be optimized to avoid needlessly copying undefined memory.336 // TODO This can be optimized to avoid needlessly copying undefined memory.
325 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);337 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);
326 self.items.ptr = new_memory.ptr;338 self.items.ptr = new_memory.ptr;
327 self.capacity = new_memory.len;339 self.capacity = new_memory.len;
340 } else {
341 self.capacity = std.math.maxInt(usize);
342 }
328 }343 }
329344
330 /// Modify the array so that it can hold at least `additional_count` **more** items.345 /// Modify the array so that it can hold at least `additional_count` **more** items.
...@@ -1299,3 +1314,23 @@ test "ArrayListAligned/ArrayListAlignedUnmanaged accepts unaligned slices" {...@@ -1299,3 +1314,23 @@ test "ArrayListAligned/ArrayListAlignedUnmanaged accepts unaligned slices" {
1299 try testing.expectEqualSlices(u8, list.items, &.{ 0, 8, 9, 6, 7, 2, 3 });1314 try testing.expectEqualSlices(u8, list.items, &.{ 0, 8, 9, 6, 7, 2, 3 });
1300 }1315 }
1301}1316}
1317
1318test "std.ArrayList(u0)" {
1319 // An ArrayList on zero-sized types should not need to allocate
1320 const a = &testing.FailingAllocator.init(testing.allocator, 0).allocator;
1321
1322 var list = ArrayList(u0).init(a);
1323 defer list.deinit();
1324
1325 try list.append(0);
1326 try list.append(0);
1327 try list.append(0);
1328 try testing.expectEqual(list.items.len, 3);
1329
1330 var count: usize = 0;
1331 for (list.items) |x| {
1332 try testing.expectEqual(x, 0);
1333 count += 1;
1334 }
1335 try testing.expectEqual(count, 3);
1336}