| ... | ... | @@ -6,21 +6,6 @@ const mem = std.mem; |
| 6 | 6 | const math = std.math; |
| 7 | 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 | 9 | /// A contiguous, growable list of items in memory. |
| 25 | 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 | 162 | self.items[n] = item; |
| 178 | 163 | } |
| 179 | 164 | |
| 180 | | /// Resize the array, adding `count` new elements at position `index`, which have `undefined` values. |
| 181 | | /// The return value is a slice pointing to the newly allocated elements. The returned pointer |
| 182 | | /// becomes invalid when the list is resized. Resizes list if self.capacity is not large enough. |
| 183 | | pub fn addManyAtIndex( |
| 184 | | self: *Self, |
| 185 | | index: usize, |
| 186 | | count: usize, |
| 187 | | ) Allocator.Error![]T { |
| 165 | /// Add `count` new elements at position `index`, which have |
| 166 | /// `undefined` values. Returns a slice pointing to the newly allocated |
| 167 | /// elements, which becomes invalid after various `ArrayList` |
| 168 | /// operations. |
| 169 | /// Invalidates pre-existing pointers to elements at and after `index`. |
| 170 | /// Invalidates all pre-existing element pointers if capacity must be |
| 171 | /// increased to accomodate the new elements. |
| 172 | pub fn addManyAt(self: *Self, index: usize, count: usize) Allocator.Error![]T { |
| 188 | 173 | const new_len = self.items.len + count; |
| 189 | | const to_move = self.items[index..]; |
| 190 | 174 | |
| 191 | | if (self.capacity >= new_len) { |
| 192 | | //There is enough space |
| 193 | | self.items.len = new_len; |
| 194 | | mem.copyBackwards( |
| 195 | | T, |
| 196 | | self.items[index + count ..], |
| 197 | | to_move, |
| 198 | | ); |
| 199 | | const result = self.items[index..][0..count]; |
| 200 | | @memset(result, undefined); |
| 201 | | return result; |
| 202 | | } else { |
| 203 | | const better_capacity = computeBetterCapacity(self.capacity, new_len); |
| 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 | | } |
| 175 | if (self.capacity >= new_len) |
| 176 | return addManyAtAssumeCapacity(self, index, count); |
| 177 | |
| 178 | // Here we avoid copying allocated but unused bytes by |
| 179 | // attempting a resize in place, and falling back to allocating |
| 180 | // a new buffer and doing our own copy. With a realloc() call, |
| 181 | // the allocator implementation would pointlessly copy our |
| 182 | // extra capacity. |
| 183 | const new_capacity = growCapacity(self.capacity, new_len); |
| 184 | const old_memory = self.allocatedSlice(); |
| 185 | if (self.allocator.resize(old_memory, new_capacity)) { |
| 186 | self.capacity = new_capacity; |
| 187 | return addManyAtAssumeCapacity(self, index, count); |
| 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 | 222 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 254 | 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 | 227 | pub fn insertSlice( |
| 257 | 228 | self: *Self, |
| 258 | 229 | index: usize, |
| 259 | 230 | items: []const T, |
| 260 | 231 | ) Allocator.Error!void { |
| 261 | | const dst = try self.addManyAtIndex( |
| 262 | | index, |
| 263 | | items.len, |
| 264 | | ); |
| 232 | const dst = try self.addManyAt(index, items.len); |
| 265 | 233 | @memcpy(dst, items); |
| 266 | 234 | } |
| 267 | 235 | |
| ... | ... | @@ -462,7 +430,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 462 | 430 | |
| 463 | 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 | 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 | 718 | self.items[n] = item; |
| 751 | 719 | } |
| 752 | 720 | |
| 753 | | /// Resize the array, adding `count` new elements at position `index`, which have `undefined` values. |
| 754 | | /// The return value is a slice pointing to the newly allocated elements. The returned pointer |
| 755 | | /// becomes invalid when the list is resized. Resizes list if self.capacity is not large enough. |
| 756 | | pub fn addManyAtIndex( |
| 721 | /// Add `count` new elements at position `index`, which have |
| 722 | /// `undefined` values. Returns a slice pointing to the newly allocated |
| 723 | /// elements, which becomes invalid after various `ArrayList` |
| 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 | 729 | self: *Self, |
| 758 | 730 | allocator: Allocator, |
| 759 | 731 | index: usize, |
| ... | ... | @@ -761,19 +733,39 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 761 | 733 | ) Allocator.Error![]T { |
| 762 | 734 | var managed = self.toManaged(allocator); |
| 763 | 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 | 757 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 768 | 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 | 762 | pub fn insertSlice( |
| 771 | 763 | self: *Self, |
| 772 | 764 | allocator: Allocator, |
| 773 | 765 | index: usize, |
| 774 | 766 | items: []const T, |
| 775 | 767 | ) Allocator.Error!void { |
| 776 | | const dst = try self.addManyAtIndex( |
| 768 | const dst = try self.addManyAt( |
| 777 | 769 | allocator, |
| 778 | 770 | index, |
| 779 | 771 | items.len, |
| ... | ... | @@ -785,7 +777,13 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 785 | 777 | /// Grows list if `len < new_items.len`. |
| 786 | 778 | /// Shrinks list if `len > new_items.len` |
| 787 | 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 | 787 | var managed = self.toManaged(allocator); |
| 790 | 788 | defer self.* = managed.moveToUnmanaged(); |
| 791 | 789 | try managed.replaceRange(start, len, new_items); |
| ... | ... | @@ -981,7 +979,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 981 | 979 | pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { |
| 982 | 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 | 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 | 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 | 1152 | test "std.ArrayList/ArrayListUnmanaged.init" { |
| 1144 | 1153 | { |
| 1145 | 1154 | var list = ArrayList(i32).init(testing.allocator); |