| ... | @@ -6,21 +6,6 @@ const mem = std.mem; | ... | @@ -6,21 +6,6 @@ const mem = std.mem; |
| 6 | const math = std.math; | 6 | const math = std.math; |
| 7 | const Allocator = mem.Allocator; | 7 | const Allocator = mem.Allocator; |
| 8 | | 8 | |
| 9 | /// Shared between managed and unmanaged versions of ArrayList. Called | | |
| 10 | /// when memory growth is necessary. Returns a capacity larger than minimum | | |
| 11 | /// that is better according to our growth policy. | | |
| 12 | fn computeBetterCapacity( | | |
| 13 | current_capacity: usize, | | |
| 14 | minimum_capacity: usize, | | |
| 15 | ) usize { | | |
| 16 | var better_capacity = current_capacity; | | |
| 17 | while (true) { | | |
| 18 | better_capacity +|= better_capacity / 2 + 8; | | |
| 19 | if (better_capacity >= minimum_capacity) | | |
| 20 | return better_capacity; | | |
| 21 | } | | |
| 22 | } | | |
| 23 | | | |
| 24 | /// A contiguous, growable list of items in memory. | 9 | /// A contiguous, growable list of items in memory. |
| 25 | /// This is a wrapper around an array of T values. Initialize with `init`. | 10 | /// This is a wrapper around an array of T values. Initialize with `init`. |
| 26 | /// | 11 | /// |
| ... | @@ -177,91 +162,74 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -177,91 +162,74 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 177 | self.items[n] = item; | 162 | self.items[n] = item; |
| 178 | } | 163 | } |
| 179 | | 164 | |
| 180 | /// Resize the array, adding `count` new elements at position `index`, which have `undefined` values. | 165 | /// Add `count` new elements at position `index`, which have |
| 181 | /// The return value is a slice pointing to the newly allocated elements. The returned pointer | 166 | /// `undefined` values. Returns a slice pointing to the newly allocated |
| 182 | /// becomes invalid when the list is resized. Resizes list if self.capacity is not large enough. | 167 | /// elements, which becomes invalid after various `ArrayList` |
| 183 | pub fn addManyAtIndex( | 168 | /// operations. |
| 184 | self: *Self, | 169 | /// Invalidates pre-existing pointers to elements at and after `index`. |
| 185 | index: usize, | 170 | /// Invalidates all pre-existing element pointers if capacity must be |
| 186 | count: usize, | 171 | /// increased to accomodate the new elements. |
| 187 | ) Allocator.Error![]T { | 172 | pub fn addManyAt(self: *Self, index: usize, count: usize) Allocator.Error![]T { |
| 188 | const new_len = self.items.len + count; | 173 | const new_len = self.items.len + count; |
| 189 | const to_move = self.items[index..]; | | |
| 190 | | 174 | |
| 191 | if (self.capacity >= new_len) { | 175 | if (self.capacity >= new_len) |
| 192 | //There is enough space | 176 | return addManyAtAssumeCapacity(self, index, count); |
| 193 | self.items.len = new_len; | 177 | |
| 194 | mem.copyBackwards( | 178 | // Here we avoid copying allocated but unused bytes by |
| 195 | T, | 179 | // attempting a resize in place, and falling back to allocating |
| 196 | self.items[index + count ..], | 180 | // a new buffer and doing our own copy. With a realloc() call, |
| 197 | to_move, | 181 | // the allocator implementation would pointlessly copy our |
| 198 | ); | 182 | // extra capacity. |
| 199 | const result = self.items[index..][0..count]; | 183 | const new_capacity = growCapacity(self.capacity, new_len); |
| 200 | @memset(result, undefined); | 184 | const old_memory = self.allocatedSlice(); |
| 201 | return result; | 185 | if (self.allocator.resize(old_memory, new_capacity)) { |
| 202 | } else { | 186 | self.capacity = new_capacity; |
| 203 | const better_capacity = computeBetterCapacity(self.capacity, new_len); | 187 | return addManyAtAssumeCapacity(self, index, count); |
| 204 | | | |
| 205 | // Here we avoid copying allocated but unused bytes by | | |
| 206 | // attempting a resize in place, and falling back to allocating | | |
| 207 | // a new buffer and doing our own copy. With a realloc() call, | | |
| 208 | // the allocator implementation would pointlessly copy our | | |
| 209 | // extra capacity. | | |
| 210 | const old_memory = self.allocatedSlice(); | | |
| 211 | if (self.allocator.resize(old_memory, better_capacity)) { | | |
| 212 | self.capacity = better_capacity; | | |
| 213 | self.items.len = new_len; | | |
| 214 | mem.copyBackwards( | | |
| 215 | T, | | |
| 216 | self.items[index + count ..], | | |
| 217 | to_move, | | |
| 218 | ); | | |
| 219 | const result = self.items[index..][0..count]; | | |
| 220 | @memset(result, undefined); | | |
| 221 | return result; | | |
| 222 | } else { | | |
| 223 | // Need a new allocation. We don't call ensureTotalCapacity because there | | |
| 224 | // would be an unnecessary check if the capacity is enough (we already | | |
| 225 | // know it's not). | | |
| 226 | const new_memory = try self.allocator.alignedAlloc( | | |
| 227 | T, | | |
| 228 | alignment, | | |
| 229 | better_capacity, | | |
| 230 | ); | | |
| 231 | @memcpy( | | |
| 232 | new_memory[0..index], | | |
| 233 | self.items[0..index], | | |
| 234 | ); | | |
| 235 | | | |
| 236 | // No need to mem.copyBackwards, as this is a new allocation. | | |
| 237 | @memcpy( | | |
| 238 | new_memory[index + count ..][0..to_move.len], | | |
| 239 | to_move, | | |
| 240 | ); | | |
| 241 | | | |
| 242 | self.allocator.free(old_memory); | | |
| 243 | self.items.ptr = new_memory.ptr; | | |
| 244 | self.items.len = new_len; | | |
| 245 | self.capacity = new_memory.len; | | |
| 246 | const result = new_memory[index..][0..count]; | | |
| 247 | @memset(result, undefined); | | |
| 248 | return result; | | |
| 249 | } | | |
| 250 | } | 188 | } |
| | 189 | |
| | 190 | // Make a new allocation, avoiding `ensureTotalCapacity` in order |
| | 191 | // to avoid extra memory copies. |
| | 192 | const new_memory = try self.allocator.alignedAlloc(T, alignment, new_capacity); |
| | 193 | const to_move = self.items[index..]; |
| | 194 | @memcpy(new_memory[0..index], self.items[0..index]); |
| | 195 | @memcpy(new_memory[index + count ..][0..to_move.len], to_move); |
| | 196 | self.allocator.free(old_memory); |
| | 197 | self.items = new_memory[0..new_len]; |
| | 198 | self.capacity = new_memory.len; |
| | 199 | // The inserted elements at `new_memory[index..][0..count]` have |
| | 200 | // already been set to `undefined` by memory allocation. |
| | 201 | return new_memory[index..][0..count]; |
| | 202 | } |
| | 203 | |
| | 204 | /// Add `count` new elements at position `index`, which have |
| | 205 | /// `undefined` values. Returns a slice pointing to the newly allocated |
| | 206 | /// elements, which becomes invalid after various `ArrayList` |
| | 207 | /// operations. |
| | 208 | /// Asserts that there is enough capacity for the new elements. |
| | 209 | /// Invalidates pre-existing pointers to elements at and after `index`, but |
| | 210 | /// does not invalidate any before that. |
| | 211 | pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T { |
| | 212 | const new_len = self.items.len + count; |
| | 213 | assert(self.capacity >= new_len); |
| | 214 | const to_move = self.items[index..]; |
| | 215 | self.items.len = new_len; |
| | 216 | mem.copyBackwards(T, self.items[index + count ..], to_move); |
| | 217 | const result = self.items[index..][0..count]; |
| | 218 | @memset(result, undefined); |
| | 219 | return result; |
| 251 | } | 220 | } |
| 252 | | 221 | |
| 253 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. | 222 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 254 | /// This operation is O(N). | 223 | /// This operation is O(N). |
| 255 | /// Invalidates pointers if additional memory is needed. | 224 | /// Invalidates pre-existing pointers to elements at and after `index`. |
| | 225 | /// Invalidates all pre-existing element pointers if capacity must be |
| | 226 | /// increased to accomodate the new elements. |
| 256 | pub fn insertSlice( | 227 | pub fn insertSlice( |
| 257 | self: *Self, | 228 | self: *Self, |
| 258 | index: usize, | 229 | index: usize, |
| 259 | items: []const T, | 230 | items: []const T, |
| 260 | ) Allocator.Error!void { | 231 | ) Allocator.Error!void { |
| 261 | const dst = try self.addManyAtIndex( | 232 | const dst = try self.addManyAt(index, items.len); |
| 262 | index, | | |
| 263 | items.len, | | |
| 264 | ); | | |
| 265 | @memcpy(dst, items); | 233 | @memcpy(dst, items); |
| 266 | } | 234 | } |
| 267 | | 235 | |
| ... | @@ -462,7 +430,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { | ... | @@ -462,7 +430,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 462 | | 430 | |
| 463 | if (self.capacity >= new_capacity) return; | 431 | if (self.capacity >= new_capacity) return; |
| 464 | | 432 | |
| 465 | const better_capacity = computeBetterCapacity(self.capacity, new_capacity); | 433 | const better_capacity = growCapacity(self.capacity, new_capacity); |
| 466 | return self.ensureTotalCapacityPrecise(better_capacity); | 434 | return self.ensureTotalCapacityPrecise(better_capacity); |
| 467 | } | 435 | } |
| 468 | | 436 | |
| ... | @@ -750,10 +718,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -750,10 +718,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 750 | self.items[n] = item; | 718 | self.items[n] = item; |
| 751 | } | 719 | } |
| 752 | | 720 | |
| 753 | /// Resize the array, adding `count` new elements at position `index`, which have `undefined` values. | 721 | /// Add `count` new elements at position `index`, which have |
| 754 | /// The return value is a slice pointing to the newly allocated elements. The returned pointer | 722 | /// `undefined` values. Returns a slice pointing to the newly allocated |
| 755 | /// becomes invalid when the list is resized. Resizes list if self.capacity is not large enough. | 723 | /// elements, which becomes invalid after various `ArrayList` |
| 756 | pub fn addManyAtIndex( | 724 | /// operations. |
| | 725 | /// Invalidates pre-existing pointers to elements at and after `index`. |
| | 726 | /// Invalidates all pre-existing element pointers if capacity must be |
| | 727 | /// increased to accomodate the new elements. |
| | 728 | pub fn addManyAt( |
| 757 | self: *Self, | 729 | self: *Self, |
| 758 | allocator: Allocator, | 730 | allocator: Allocator, |
| 759 | index: usize, | 731 | index: usize, |
| ... | @@ -761,19 +733,39 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -761,19 +733,39 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 761 | ) Allocator.Error![]T { | 733 | ) Allocator.Error![]T { |
| 762 | var managed = self.toManaged(allocator); | 734 | var managed = self.toManaged(allocator); |
| 763 | defer self.* = managed.moveToUnmanaged(); | 735 | defer self.* = managed.moveToUnmanaged(); |
| 764 | return managed.addManyAtIndex(index, count); | 736 | return managed.addManyAt(index, count); |
| | 737 | } |
| | 738 | |
| | 739 | /// Add `count` new elements at position `index`, which have |
| | 740 | /// `undefined` values. Returns a slice pointing to the newly allocated |
| | 741 | /// elements, which becomes invalid after various `ArrayList` |
| | 742 | /// operations. |
| | 743 | /// Asserts that there is enough capacity for the new elements. |
| | 744 | /// Invalidates pre-existing pointers to elements at and after `index`, but |
| | 745 | /// does not invalidate any before that. |
| | 746 | pub fn addManyAtAssumeCapacity(self: *Self, index: usize, count: usize) []T { |
| | 747 | const new_len = self.items.len + count; |
| | 748 | assert(self.capacity >= new_len); |
| | 749 | const to_move = self.items[index..]; |
| | 750 | self.items.len = new_len; |
| | 751 | mem.copyBackwards(T, self.items[index + count ..], to_move); |
| | 752 | const result = self.items[index..][0..count]; |
| | 753 | @memset(result, undefined); |
| | 754 | return result; |
| 765 | } | 755 | } |
| 766 | | 756 | |
| 767 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. | 757 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 768 | /// This operation is O(N). | 758 | /// This operation is O(N). |
| 769 | /// Invalidates pointers if additional memory is needed. | 759 | /// Invalidates pre-existing pointers to elements at and after `index`. |
| | 760 | /// Invalidates all pre-existing element pointers if capacity must be |
| | 761 | /// increased to accomodate the new elements. |
| 770 | pub fn insertSlice( | 762 | pub fn insertSlice( |
| 771 | self: *Self, | 763 | self: *Self, |
| 772 | allocator: Allocator, | 764 | allocator: Allocator, |
| 773 | index: usize, | 765 | index: usize, |
| 774 | items: []const T, | 766 | items: []const T, |
| 775 | ) Allocator.Error!void { | 767 | ) Allocator.Error!void { |
| 776 | const dst = try self.addManyAtIndex( | 768 | const dst = try self.addManyAt( |
| 777 | allocator, | 769 | allocator, |
| 778 | index, | 770 | index, |
| 779 | items.len, | 771 | items.len, |
| ... | @@ -785,7 +777,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -785,7 +777,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 785 | /// Grows list if `len < new_items.len`. | 777 | /// Grows list if `len < new_items.len`. |
| 786 | /// Shrinks list if `len > new_items.len` | 778 | /// Shrinks list if `len > new_items.len` |
| 787 | /// Invalidates pointers if this ArrayList is resized. | 779 | /// Invalidates pointers if this ArrayList is resized. |
| 788 | pub fn replaceRange(self: *Self, allocator: Allocator, start: usize, len: usize, new_items: []const T) Allocator.Error!void { | 780 | pub fn replaceRange( |
| | 781 | self: *Self, |
| | 782 | allocator: Allocator, |
| | 783 | start: usize, |
| | 784 | len: usize, |
| | 785 | new_items: []const T, |
| | 786 | ) Allocator.Error!void { |
| 789 | var managed = self.toManaged(allocator); | 787 | var managed = self.toManaged(allocator); |
| 790 | defer self.* = managed.moveToUnmanaged(); | 788 | defer self.* = managed.moveToUnmanaged(); |
| 791 | try managed.replaceRange(start, len, new_items); | 789 | try managed.replaceRange(start, len, new_items); |
| ... | @@ -981,7 +979,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -981,7 +979,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 981 | pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { | 979 | pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { |
| 982 | if (self.capacity >= new_capacity) return; | 980 | if (self.capacity >= new_capacity) return; |
| 983 | | 981 | |
| 984 | var better_capacity = computeBetterCapacity(self.capacity, new_capacity); | 982 | var better_capacity = growCapacity(self.capacity, new_capacity); |
| 985 | return self.ensureTotalCapacityPrecise(allocator, better_capacity); | 983 | return self.ensureTotalCapacityPrecise(allocator, better_capacity); |
| 986 | } | 984 | } |
| 987 | | 985 | |
| ... | @@ -1140,6 +1138,17 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -1140,6 +1138,17 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 1140 | }; | 1138 | }; |
| 1141 | } | 1139 | } |
| 1142 | | 1140 | |
| | 1141 | /// Called when memory growth is necessary. Returns a capacity larger than |
| | 1142 | /// minimum that grows super-linearly. |
| | 1143 | fn growCapacity(current: usize, minimum: usize) usize { |
| | 1144 | var new = current; |
| | 1145 | while (true) { |
| | 1146 | new +|= new / 2 + 8; |
| | 1147 | if (new >= minimum) |
| | 1148 | return new; |
| | 1149 | } |
| | 1150 | } |
| | 1151 | |
| 1143 | test "std.ArrayList/ArrayListUnmanaged.init" { | 1152 | test "std.ArrayList/ArrayListUnmanaged.init" { |
| 1144 | { | 1153 | { |
| 1145 | var list = ArrayList(i32).init(testing.allocator); | 1154 | var list = ArrayList(i32).init(testing.allocator); |