| ... | ... | @@ -6,6 +6,21 @@ 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 | |
| 9 | 24 | /// A contiguous, growable list of items in memory. |
| 10 | 25 | /// This is a wrapper around an array of T values. Initialize with `init`. |
| 11 | 26 | /// |
| ... | ... | @@ -162,15 +177,92 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 162 | 177 | self.items[n] = item; |
| 163 | 178 | } |
| 164 | 179 | |
| 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 { |
| 188 | const new_len = self.items.len + count; |
| 189 | const to_move = self.items[index..]; |
| 190 | |
| 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 | } |
| 250 | } |
| 251 | } |
| 252 | |
| 165 | 253 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 166 | 254 | /// This operation is O(N). |
| 167 | 255 | /// Invalidates pointers if additional memory is needed. |
| 168 | | pub fn insertSlice(self: *Self, i: usize, items: []const T) Allocator.Error!void { |
| 169 | | try self.ensureUnusedCapacity(items.len); |
| 170 | | self.items.len += items.len; |
| 171 | | |
| 172 | | mem.copyBackwards(T, self.items[i + items.len .. self.items.len], self.items[i .. self.items.len - items.len]); |
| 173 | | @memcpy(self.items[i..][0..items.len], items); |
| 256 | pub fn insertSlice( |
| 257 | self: *Self, |
| 258 | index: usize, |
| 259 | items: []const T, |
| 260 | ) Allocator.Error!void { |
| 261 | const dst = try self.addManyAtIndex( |
| 262 | index, |
| 263 | items.len, |
| 264 | ); |
| 265 | @memcpy(dst, items); |
| 174 | 266 | } |
| 175 | 267 | |
| 176 | 268 | /// Replace range of elements `list[start..][0..len]` with `new_items`. |
| ... | ... | @@ -370,12 +462,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 370 | 462 | |
| 371 | 463 | if (self.capacity >= new_capacity) return; |
| 372 | 464 | |
| 373 | | var better_capacity = self.capacity; |
| 374 | | while (true) { |
| 375 | | better_capacity +|= better_capacity / 2 + 8; |
| 376 | | if (better_capacity >= new_capacity) break; |
| 377 | | } |
| 378 | | |
| 465 | const better_capacity = computeBetterCapacity(self.capacity, new_capacity); |
| 379 | 466 | return self.ensureTotalCapacityPrecise(better_capacity); |
| 380 | 467 | } |
| 381 | 468 | |
| ... | ... | @@ -663,16 +750,35 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 663 | 750 | self.items[n] = item; |
| 664 | 751 | } |
| 665 | 752 | |
| 666 | | /// Insert slice `items` at index `i`. Moves `list[i .. list.len]` to |
| 667 | | /// higher indicices make room. |
| 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( |
| 757 | self: *Self, |
| 758 | allocator: Allocator, |
| 759 | index: usize, |
| 760 | count: usize, |
| 761 | ) Allocator.Error![]T { |
| 762 | var managed = self.toManaged(allocator); |
| 763 | defer self.* = managed.moveToUnmanaged(); |
| 764 | return managed.addManyAtIndex(index, count); |
| 765 | } |
| 766 | |
| 767 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 668 | 768 | /// This operation is O(N). |
| 669 | 769 | /// Invalidates pointers if additional memory is needed. |
| 670 | | pub fn insertSlice(self: *Self, allocator: Allocator, i: usize, items: []const T) Allocator.Error!void { |
| 671 | | try self.ensureUnusedCapacity(allocator, items.len); |
| 672 | | self.items.len += items.len; |
| 673 | | |
| 674 | | mem.copyBackwards(T, self.items[i + items.len .. self.items.len], self.items[i .. self.items.len - items.len]); |
| 675 | | @memcpy(self.items[i..][0..items.len], items); |
| 770 | pub fn insertSlice( |
| 771 | self: *Self, |
| 772 | allocator: Allocator, |
| 773 | index: usize, |
| 774 | items: []const T, |
| 775 | ) Allocator.Error!void { |
| 776 | const dst = try self.addManyAtIndex( |
| 777 | allocator, |
| 778 | index, |
| 779 | items.len, |
| 780 | ); |
| 781 | @memcpy(dst, items); |
| 676 | 782 | } |
| 677 | 783 | |
| 678 | 784 | /// Replace range of elements `list[start..][0..len]` with `new_items` |
| ... | ... | @@ -681,8 +787,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 681 | 787 | /// Invalidates pointers if this ArrayList is resized. |
| 682 | 788 | pub fn replaceRange(self: *Self, allocator: Allocator, start: usize, len: usize, new_items: []const T) Allocator.Error!void { |
| 683 | 789 | var managed = self.toManaged(allocator); |
| 790 | defer self.* = managed.moveToUnmanaged(); |
| 684 | 791 | try managed.replaceRange(start, len, new_items); |
| 685 | | self.* = managed.moveToUnmanaged(); |
| 686 | 792 | } |
| 687 | 793 | |
| 688 | 794 | /// Extend the list by 1 element. Allocates more memory as necessary. |
| ... | ... | @@ -875,12 +981,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 875 | 981 | pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { |
| 876 | 982 | if (self.capacity >= new_capacity) return; |
| 877 | 983 | |
| 878 | | var better_capacity = self.capacity; |
| 879 | | while (true) { |
| 880 | | better_capacity +|= better_capacity / 2 + 8; |
| 881 | | if (better_capacity >= new_capacity) break; |
| 882 | | } |
| 883 | | |
| 984 | var better_capacity = computeBetterCapacity(self.capacity, new_capacity); |
| 884 | 985 | return self.ensureTotalCapacityPrecise(allocator, better_capacity); |
| 885 | 986 | } |
| 886 | 987 | |
| ... | ... | @@ -1650,6 +1751,40 @@ test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" { |
| 1650 | 1751 | } |
| 1651 | 1752 | } |
| 1652 | 1753 | |
| 1754 | test "std.ArrayList/ArrayListUnmanaged growing memory preserves contents" { |
| 1755 | const a = std.testing.allocator; |
| 1756 | { |
| 1757 | var list = ArrayList(u8).init(a); |
| 1758 | defer list.deinit(); |
| 1759 | try list.ensureTotalCapacityPrecise(1); |
| 1760 | |
| 1761 | (try list.addManyAsArray(4)).* = "abcd".*; |
| 1762 | try list.ensureTotalCapacityPrecise(4); |
| 1763 | |
| 1764 | try list.appendSlice("efgh"); |
| 1765 | try testing.expectEqualSlices(u8, list.items, "abcdefgh"); |
| 1766 | try list.ensureTotalCapacityPrecise(8); |
| 1767 | |
| 1768 | try list.insertSlice(4, "ijkl"); |
| 1769 | try testing.expectEqualSlices(u8, list.items, "abcdijklefgh"); |
| 1770 | } |
| 1771 | { |
| 1772 | var list = ArrayListUnmanaged(u8){}; |
| 1773 | try list.ensureTotalCapacityPrecise(a, 1); |
| 1774 | defer list.deinit(a); |
| 1775 | |
| 1776 | (try list.addManyAsArray(a, 4)).* = "abcd".*; |
| 1777 | try list.ensureTotalCapacityPrecise(a, 4); |
| 1778 | |
| 1779 | try list.appendSlice(a, "efgh"); |
| 1780 | try testing.expectEqualSlices(u8, list.items, "abcdefgh"); |
| 1781 | try list.ensureTotalCapacityPrecise(a, 8); |
| 1782 | |
| 1783 | try list.insertSlice(a, 4, "ijkl"); |
| 1784 | try testing.expectEqualSlices(u8, list.items, "abcdijklefgh"); |
| 1785 | } |
| 1786 | } |
| 1787 | |
| 1653 | 1788 | test "std.ArrayList/ArrayList.fromOwnedSliceSentinel" { |
| 1654 | 1789 | const a = testing.allocator; |
| 1655 | 1790 | |