authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2021-11-01 00:38:11-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2021-11-01 00:57:24-07:00
logf49d42729a22846ec54b6610db415ac8cdaa31db
treecfd5a54c5d1dbaed017511a740fd4f4cf5593cf9
parent83dcfd62053a80024b1cb282fddf399a36c9c58e

std.ArrayList: add ensureTotalCapacityPrecise and update doc comments

initCapacity did and still does use the ensureTotalCapacityPrecise logic because the initial capacity of an ArrayList is not important in terms of how it grows, so allocating a more exact slice up-front allows for saving memory when the array list never exceeds that initial allocation size. There are use cases where this precise capacity is useful outside of the `init` function, though, like in instances where the user does not call the `init` function themselves but otherwise knows that an ArrayList is empty so calling `ensureTotalCapacityPrecise` can give the same memory savings that `initCapacity` would have. Closes #9775

1 files changed, 30 insertions(+), 17 deletions(-)

lib/std/array_list.zig+30-17
...@@ -56,19 +56,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -56,19 +56,11 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
56 }56 }
5757
58 /// Initialize with capacity to hold at least `num` elements.58 /// Initialize with capacity to hold at least `num` elements.
59 /// The resulting capacity is likely to be equal to `num`.
59 /// Deinitialize with `deinit` or use `toOwnedSlice`.60 /// Deinitialize with `deinit` or use `toOwnedSlice`.
60 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {61 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
61 var self = Self.init(allocator);62 var self = Self.init(allocator);
6263 try self.ensureTotalCapacityPrecise(num);
63 if (@sizeOf(T) > 0) {
64 const new_memory = try self.allocator.allocAdvanced(T, alignment, num, .at_least);
65 self.items.ptr = new_memory.ptr;
66 self.capacity = new_memory.len;
67 } else {
68 // If `T` is a zero-sized type, then we do not need to allocate memory.
69 self.capacity = std.math.maxInt(usize);
70 }
71
72 return self;64 return self;
73 }65 }
7466
...@@ -330,8 +322,22 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -330,8 +322,22 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
330 if (better_capacity >= new_capacity) break;322 if (better_capacity >= new_capacity) break;
331 }323 }
332324
325 return self.ensureTotalCapacityPrecise(better_capacity);
326 } else {
327 self.capacity = std.math.maxInt(usize);
328 }
329 }
330
331 /// Modify the array so that it can hold at least `new_capacity` items.
332 /// Like `ensureTotalCapacity`, but the resulting capacity is much more likely
333 /// (but not guaranteed) to be equal to `new_capacity`.
334 /// Invalidates pointers if additional memory is needed.
335 pub fn ensureTotalCapacityPrecise(self: *Self, new_capacity: usize) !void {
336 if (@sizeOf(T) > 0) {
337 if (self.capacity >= new_capacity) return;
338
333 // TODO This can be optimized to avoid needlessly copying undefined memory.339 // TODO This can be optimized to avoid needlessly copying undefined memory.
334 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);340 const new_memory = try self.allocator.reallocAtLeast(self.allocatedSlice(), new_capacity);
335 self.items.ptr = new_memory.ptr;341 self.items.ptr = new_memory.ptr;
336 self.capacity = new_memory.len;342 self.capacity = new_memory.len;
337 } else {343 } else {
...@@ -464,14 +470,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -464,14 +470,11 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
464 pub const Slice = if (alignment) |a| ([]align(a) T) else []T;470 pub const Slice = if (alignment) |a| ([]align(a) T) else []T;
465471
466 /// Initialize with capacity to hold at least num elements.472 /// Initialize with capacity to hold at least num elements.
473 /// The resulting capacity is likely to be equal to `num`.
467 /// Deinitialize with `deinit` or use `toOwnedSlice`.474 /// Deinitialize with `deinit` or use `toOwnedSlice`.
468 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {475 pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
469 var self = Self{};476 var self = Self{};
470477 try self.ensureTotalCapacityPrecise(allocator, num);
471 const new_memory = try allocator.allocAdvanced(T, alignment, num, .at_least);
472 self.items.ptr = new_memory.ptr;
473 self.capacity = new_memory.len;
474
475 return self;478 return self;
476 }479 }
477480
...@@ -685,7 +688,17 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -685,7 +688,17 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
685 if (better_capacity >= new_capacity) break;688 if (better_capacity >= new_capacity) break;
686 }689 }
687690
688 const new_memory = try allocator.reallocAtLeast(self.allocatedSlice(), better_capacity);691 return self.ensureTotalCapacityPrecise(allocator, better_capacity);
692 }
693
694 /// Modify the array so that it can hold at least `new_capacity` items.
695 /// Like `ensureTotalCapacity`, but the resulting capacity is much more likely
696 /// (but not guaranteed) to be equal to `new_capacity`.
697 /// Invalidates pointers if additional memory is needed.
698 pub fn ensureTotalCapacityPrecise(self: *Self, allocator: *Allocator, new_capacity: usize) !void {
699 if (self.capacity >= new_capacity) return;
700
701 const new_memory = try allocator.reallocAtLeast(self.allocatedSlice(), new_capacity);
689 self.items.ptr = new_memory.ptr;702 self.items.ptr = new_memory.ptr;
690 self.capacity = new_memory.len;703 self.capacity = new_memory.len;
691 }704 }