| ... | @@ -6,6 +6,21 @@ const mem = std.mem; | ... | @@ -6,6 +6,21 @@ 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 | |
| 9 | /// A contiguous, growable list of items in memory. | 24 | /// A contiguous, growable list of items in memory. |
| 10 | /// This is a wrapper around an array of T values. Initialize with `init`. | 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,15 +177,92 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 162 | self.items[n] = item; | 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 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. | 253 | /// Insert slice `items` at index `i` by moving `list[i .. list.len]` to make room. |
| 166 | /// This operation is O(N). | 254 | /// This operation is O(N). |
| 167 | /// Invalidates pointers if additional memory is needed. | 255 | /// Invalidates pointers if additional memory is needed. |
| 168 | pub fn insertSlice(self: *Self, i: usize, items: []const T) Allocator.Error!void { | 256 | pub fn insertSlice( |
| 169 | try self.ensureUnusedCapacity(items.len); | 257 | self: *Self, |
| 170 | self.items.len += items.len; | 258 | index: usize, |
| 171 | | 259 | items: []const T, |
| 172 | mem.copyBackwards(T, self.items[i + items.len .. self.items.len], self.items[i .. self.items.len - items.len]); | 260 | ) Allocator.Error!void { |
| 173 | @memcpy(self.items[i..][0..items.len], items); | 261 | const dst = try self.addManyAtIndex( |
| | 262 | index, |
| | 263 | items.len, |
| | 264 | ); |
| | 265 | @memcpy(dst, items); |
| 174 | } | 266 | } |
| 175 | | 267 | |
| 176 | /// Replace range of elements `list[start..][0..len]` with `new_items`. | 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,12 +462,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 370 | | 462 | |
| 371 | if (self.capacity >= new_capacity) return; | 463 | if (self.capacity >= new_capacity) return; |
| 372 | | 464 | |
| 373 | var better_capacity = self.capacity; | 465 | const better_capacity = computeBetterCapacity(self.capacity, new_capacity); |
| 374 | while (true) { | | |
| 375 | better_capacity +|= better_capacity / 2 + 8; | | |
| 376 | if (better_capacity >= new_capacity) break; | | |
| 377 | } | | |
| 378 | | | |
| 379 | return self.ensureTotalCapacityPrecise(better_capacity); | 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,16 +750,35 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 663 | self.items[n] = item; | 750 | self.items[n] = item; |
| 664 | } | 751 | } |
| 665 | | 752 | |
| 666 | /// Insert slice `items` at index `i`. Moves `list[i .. list.len]` to | 753 | /// Resize the array, adding `count` new elements at position `index`, which have `undefined` values. |
| 667 | /// higher indicices make room. | 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 | /// This operation is O(N). | 768 | /// This operation is O(N). |
| 669 | /// Invalidates pointers if additional memory is needed. | 769 | /// Invalidates pointers if additional memory is needed. |
| 670 | pub fn insertSlice(self: *Self, allocator: Allocator, i: usize, items: []const T) Allocator.Error!void { | 770 | pub fn insertSlice( |
| 671 | try self.ensureUnusedCapacity(allocator, items.len); | 771 | self: *Self, |
| 672 | self.items.len += items.len; | 772 | allocator: Allocator, |
| 673 | | 773 | index: usize, |
| 674 | mem.copyBackwards(T, self.items[i + items.len .. self.items.len], self.items[i .. self.items.len - items.len]); | 774 | items: []const T, |
| 675 | @memcpy(self.items[i..][0..items.len], items); | 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 | /// Replace range of elements `list[start..][0..len]` with `new_items` | 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,8 +787,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 681 | /// Invalidates pointers if this ArrayList is resized. | 787 | /// Invalidates pointers if this ArrayList is resized. |
| 682 | pub fn replaceRange(self: *Self, allocator: Allocator, start: usize, len: usize, new_items: []const T) Allocator.Error!void { | 788 | pub fn replaceRange(self: *Self, allocator: Allocator, start: usize, len: usize, new_items: []const T) Allocator.Error!void { |
| 683 | var managed = self.toManaged(allocator); | 789 | var managed = self.toManaged(allocator); |
| | 790 | defer self.* = managed.moveToUnmanaged(); |
| 684 | try managed.replaceRange(start, len, new_items); | 791 | try managed.replaceRange(start, len, new_items); |
| 685 | self.* = managed.moveToUnmanaged(); | | |
| 686 | } | 792 | } |
| 687 | | 793 | |
| 688 | /// Extend the list by 1 element. Allocates more memory as necessary. | 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,12 +981,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 875 | pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { | 981 | pub fn ensureTotalCapacity(self: *Self, allocator: Allocator, new_capacity: usize) Allocator.Error!void { |
| 876 | if (self.capacity >= new_capacity) return; | 982 | if (self.capacity >= new_capacity) return; |
| 877 | | 983 | |
| 878 | var better_capacity = self.capacity; | 984 | var better_capacity = computeBetterCapacity(self.capacity, new_capacity); |
| 879 | while (true) { | | |
| 880 | better_capacity +|= better_capacity / 2 + 8; | | |
| 881 | if (better_capacity >= new_capacity) break; | | |
| 882 | } | | |
| 883 | | | |
| 884 | return self.ensureTotalCapacityPrecise(allocator, better_capacity); | 985 | return self.ensureTotalCapacityPrecise(allocator, better_capacity); |
| 885 | } | 986 | } |
| 886 | | 987 | |
| ... | @@ -1650,6 +1751,40 @@ test "std.ArrayList/ArrayListUnmanaged.addManyAsArray" { | ... | @@ -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 | test "std.ArrayList/ArrayList.fromOwnedSliceSentinel" { | 1788 | test "std.ArrayList/ArrayList.fromOwnedSliceSentinel" { |
| 1654 | const a = testing.allocator; | 1789 | const a = testing.allocator; |
| 1655 | | 1790 | |