authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-30 15:42:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-30 15:42:59-07:00
log16caea38d1fe0fb1ea7f18e6344f21f8c11d8dfe
tree2b53170029ba95146d4def4a83ac60486c47e571
parentc84bc3a06b9a529d063518e6a4a331ba09227c8a

std.ArrayList: fix shrinkAndFree

Fixes a regression introduced in e35f297aeb993ec956ae80379ddf7f86069e109b. Now there is test coverage for ArrayList.shrinkAndFree in the case when resizing fails.

2 files changed, 33 insertions(+), 34 deletions(-)

lib/std/array_list.zig+30-32
......@@ -314,32 +314,9 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
314314 /// Reduce allocated capacity to `new_len`.
315315 /// May invalidate element pointers.
316316 pub fn shrinkAndFree(self: *Self, new_len: usize) void {
317 assert(new_len <= self.items.len);
318
319 if (@sizeOf(T) == 0) {
320 self.items.len = new_len;
321 return;
322 }
323
324 const old_memory = self.allocatedSlice();
325 if (self.allocator.resize(old_memory, new_len)) {
326 self.capacity = new_len;
327 self.items.len = new_len;
328 return;
329 }
330
331 const new_memory = self.allocator.alignedAlloc(T, alignment, new_len) catch |e| switch (e) {
332 error.OutOfMemory => {
333 // No problem, capacity is still correct then.
334 self.items.len = new_len;
335 return;
336 },
337 };
338
339 mem.copy(T, new_memory, self.items);
340 self.allocator.free(old_memory);
341 self.items = new_memory;
342 self.capacity = new_memory.len;
317 var unmanaged = self.moveToUnmanaged();
318 unmanaged.shrinkAndFree(self.allocator, new_len);
319 self.* = unmanaged.toManaged(self.allocator);
343320 }
344321
345322 /// Reduce length to `new_len`.
......@@ -782,7 +759,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
782759 },
783760 };
784761
785 mem.copy(T, new_memory, self.items);
762 mem.copy(T, new_memory, self.items[0..new_len]);
786763 allocator.free(old_memory);
787764 self.items = new_memory;
788765 self.capacity = new_memory.len;
......@@ -1488,14 +1465,18 @@ test "std.ArrayListUnmanaged(u8) implements writer" {
14881465 }
14891466}
14901467
1491test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMemory" {
1492 // use an arena allocator to make sure realloc returns error.OutOfMemory
1493 var arena = std.heap.ArenaAllocator.init(testing.allocator);
1494 defer arena.deinit();
1495 const a = arena.allocator();
1468test "shrink still sets length when resizing is disabled" {
1469 // Use the testing allocator but with resize disabled.
1470 var a = testing.allocator;
1471 a.vtable = &.{
1472 .alloc = a.vtable.alloc,
1473 .resize = Allocator.noResize,
1474 .free = a.vtable.free,
1475 };
14961476
14971477 {
14981478 var list = ArrayList(i32).init(a);
1479 defer list.deinit();
14991480
15001481 try list.append(1);
15011482 try list.append(2);
......@@ -1506,6 +1487,7 @@ test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMe
15061487 }
15071488 {
15081489 var list = ArrayListUnmanaged(i32){};
1490 defer list.deinit(a);
15091491
15101492 try list.append(a, 1);
15111493 try list.append(a, 2);
......@@ -1516,6 +1498,22 @@ test "std.ArrayList/ArrayListUnmanaged.shrink still sets length on error.OutOfMe
15161498 }
15171499}
15181500
1501test "shrinkAndFree with a copy" {
1502 // Use the testing allocator but with resize disabled.
1503 var a = testing.allocator;
1504 a.vtable = &.{
1505 .alloc = a.vtable.alloc,
1506 .resize = Allocator.noResize,
1507 .free = a.vtable.free,
1508 };
1509 var list = ArrayList(i32).init(a);
1510 defer list.deinit();
1511
1512 try list.appendNTimes(3, 16);
1513 list.shrinkAndFree(4);
1514 try testing.expect(mem.eql(i32, list.items, &.{ 3, 3, 3, 3 }));
1515}
1516
15191517test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" {
15201518 const a = std.testing.allocator;
15211519 {
lib/std/heap/arena_allocator.zig+3-2
......@@ -107,8 +107,9 @@ pub const ArenaAllocator = struct {
107107 const cur_node = self.state.buffer_list.first orelse return false;
108108 const cur_buf = cur_node.data[@sizeOf(BufNode)..];
109109 if (@ptrToInt(cur_buf.ptr) + self.state.end_index != @ptrToInt(buf.ptr) + buf.len) {
110 if (new_len > buf.len) return false;
111 return true;
110 // It's not the most recent allocation, so it cannot be expanded,
111 // but it's fine if they want to make it smaller.
112 return new_len <= buf.len;
112113 }
113114
114115 if (buf.len >= new_len) {