authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-19 21:10:09-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-01-19 21:10:09-08:00
log480a2f7f02f9dcb2e53f4515fc26fb72ad78ffd9
treeae8d01f43cedfc2e0c018b7c6b8654b4d6dc1b0c
parent46d592e485cf8ee4d85854040acd1b05271591b5
parent827e30634fcb907c584bcf68505354771068c22a
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #18293 from g-cassie/array-list-replace-range-assume

Add replaceRangeAssumeCapacity method to ArrayList

1 files changed, 121 insertions(+), 44 deletions(-)

lib/std/array_list.zig+121-44
......@@ -238,33 +238,22 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
238238 @memcpy(dst, items);
239239 }
240240
241 /// Replace range of elements `list[start..][0..len]` with `new_items`.
242 /// Grows list if `len < new_items.len`.
243 /// Shrinks list if `len > new_items.len`.
244 /// Invalidates element pointers if this ArrayList is resized.
241 /// Grows or shrinks the list as necessary.
242 /// Invalidates element pointers if additional capacity is allocated.
245243 /// Asserts that the range is in bounds.
246244 pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: []const T) Allocator.Error!void {
247 const after_range = start + len;
248 const range = self.items[start..after_range];
249
250 if (range.len == new_items.len)
251 @memcpy(range[0..new_items.len], new_items)
252 else if (range.len < new_items.len) {
253 const first = new_items[0..range.len];
254 const rest = new_items[range.len..];
255
256 @memcpy(range[0..first.len], first);
257 try self.insertSlice(after_range, rest);
258 } else {
259 @memcpy(range[0..new_items.len], new_items);
260 const after_subrange = start + new_items.len;
261
262 for (self.items[after_range..], 0..) |item, i| {
263 self.items[after_subrange..][i] = item;
264 }
245 var unmanaged = self.moveToUnmanaged();
246 defer self.* = unmanaged.toManaged(self.allocator);
247 return unmanaged.replaceRange(self.allocator, start, len, new_items);
248 }
265249
266 self.items.len -= len - new_items.len;
267 }
250 /// Grows or shrinks the list as necessary.
251 /// Never invalidates element pointers.
252 /// Asserts the capacity is enough for additional items.
253 pub fn replaceRangeAssumeCapacity(self: *Self, start: usize, len: usize, new_items: []const T) void {
254 var unmanaged = self.moveToUnmanaged();
255 defer self.* = unmanaged.toManaged(self.allocator);
256 return unmanaged.replaceRangeAssumeCapacity(start, len, new_items);
268257 }
269258
270259 /// Extends the list by 1 element. Allocates more memory as necessary.
......@@ -290,13 +279,8 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
290279 /// Asserts that the index is in bounds.
291280 /// Asserts that the list is not empty.
292281 pub fn orderedRemove(self: *Self, i: usize) T {
293 const newlen = self.items.len - 1;
294 if (newlen == i) return self.pop();
295
296282 const old_item = self.items[i];
297 for (self.items[i..newlen], 0..) |*b, j| b.* = self.items[i + 1 + j];
298 self.items[newlen] = undefined;
299 self.items.len = newlen;
283 self.replaceRangeAssumeCapacity(i, 1, &.{});
300284 return old_item;
301285 }
302286
......@@ -808,11 +792,9 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
808792 @memcpy(dst, items);
809793 }
810794
811 /// Replace range of elements `list[start..][0..len]` with `new_items`
812 /// Grows list if `len < new_items.len`.
813 /// Shrinks list if `len > new_items.len`
814 /// Invalidates element pointers if this ArrayList is resized.
815 /// Asserts that the start index is in bounds or equal to the length.
795 /// Grows or shrinks the list as necessary.
796 /// Invalidates element pointers if additional capacity is allocated.
797 /// Asserts that the range is in bounds.
816798 pub fn replaceRange(
817799 self: *Self,
818800 allocator: Allocator,
......@@ -820,9 +802,44 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
820802 len: usize,
821803 new_items: []const T,
822804 ) Allocator.Error!void {
823 var managed = self.toManaged(allocator);
824 defer self.* = managed.moveToUnmanaged();
825 try managed.replaceRange(start, len, new_items);
805 const after_range = start + len;
806 const range = self.items[start..after_range];
807 if (range.len < new_items.len) {
808 const first = new_items[0..range.len];
809 const rest = new_items[range.len..];
810 @memcpy(range[0..first.len], first);
811 try self.insertSlice(allocator, after_range, rest);
812 } else {
813 self.replaceRangeAssumeCapacity(start, len, new_items);
814 }
815 }
816
817 /// Grows or shrinks the list as necessary.
818 /// Never invalidates element pointers.
819 /// Asserts the capacity is enough for additional items.
820 pub fn replaceRangeAssumeCapacity(self: *Self, start: usize, len: usize, new_items: []const T) void {
821 const after_range = start + len;
822 const range = self.items[start..after_range];
823
824 if (range.len == new_items.len)
825 @memcpy(range[0..new_items.len], new_items)
826 else if (range.len < new_items.len) {
827 const first = new_items[0..range.len];
828 const rest = new_items[range.len..];
829 @memcpy(range[0..first.len], first);
830 const dst = self.addManyAtAssumeCapacity(after_range, rest.len);
831 @memcpy(dst, rest);
832 } else {
833 const extra = range.len - new_items.len;
834 @memcpy(range[0..new_items.len], new_items);
835 std.mem.copyForwards(
836 T,
837 self.items[after_range - extra ..],
838 self.items[after_range..],
839 );
840 @memset(self.items[self.items.len - extra ..], undefined);
841 self.items.len -= extra;
842 }
826843 }
827844
828845 /// Extend the list by 1 element. Allocates more memory as necessary.
......@@ -846,13 +863,8 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
846863 /// Asserts that the list is not empty.
847864 /// Asserts that the index is in bounds.
848865 pub fn orderedRemove(self: *Self, i: usize) T {
849 const newlen = self.items.len - 1;
850 if (newlen == i) return self.pop();
851
852866 const old_item = self.items[i];
853 for (self.items[i..newlen], 0..) |*b, j| b.* = self.items[i + 1 + j];
854 self.items[newlen] = undefined;
855 self.items.len = newlen;
867 self.replaceRangeAssumeCapacity(i, 1, &.{});
856868 return old_item;
857869 }
858870
......@@ -1470,6 +1482,22 @@ test "std.ArrayList/ArrayListUnmanaged.orderedRemove" {
14701482 try testing.expectEqual(@as(i32, 2), list.items[0]);
14711483 try testing.expectEqual(@as(usize, 4), list.items.len);
14721484 }
1485 {
1486 // remove last item
1487 var list = ArrayList(i32).init(a);
1488 defer list.deinit();
1489 try list.append(1);
1490 try testing.expectEqual(@as(i32, 1), list.orderedRemove(0));
1491 try testing.expectEqual(@as(usize, 0), list.items.len);
1492 }
1493 {
1494 // remove last item
1495 var list = ArrayListUnmanaged(i32){};
1496 defer list.deinit(a);
1497 try list.append(a, 1);
1498 try testing.expectEqual(@as(i32, 1), list.orderedRemove(0));
1499 try testing.expectEqual(@as(usize, 0), list.items.len);
1500 }
14731501}
14741502
14751503test "std.ArrayList/ArrayListUnmanaged.swapRemove" {
......@@ -1665,6 +1693,55 @@ test "std.ArrayList/ArrayListUnmanaged.replaceRange" {
16651693 try testing.expectEqualSlices(i32, list_lt.items, &result_le);
16661694 try testing.expectEqualSlices(i32, list_gt.items, &result_gt);
16671695 }
1696
1697 {
1698 var list_zero = ArrayList(i32).init(a);
1699 var list_eq = ArrayList(i32).init(a);
1700 var list_lt = ArrayList(i32).init(a);
1701 var list_gt = ArrayList(i32).init(a);
1702
1703 try list_zero.appendSlice(&init);
1704 try list_eq.appendSlice(&init);
1705 try list_lt.appendSlice(&init);
1706 try list_gt.appendSlice(&init);
1707
1708 list_zero.replaceRangeAssumeCapacity(1, 0, &new);
1709 list_eq.replaceRangeAssumeCapacity(1, 3, &new);
1710 list_lt.replaceRangeAssumeCapacity(1, 2, &new);
1711
1712 // after_range > new_items.len in function body
1713 try testing.expect(1 + 4 > new.len);
1714 list_gt.replaceRangeAssumeCapacity(1, 4, &new);
1715
1716 try testing.expectEqualSlices(i32, list_zero.items, &result_zero);
1717 try testing.expectEqualSlices(i32, list_eq.items, &result_eq);
1718 try testing.expectEqualSlices(i32, list_lt.items, &result_le);
1719 try testing.expectEqualSlices(i32, list_gt.items, &result_gt);
1720 }
1721 {
1722 var list_zero = ArrayListUnmanaged(i32){};
1723 var list_eq = ArrayListUnmanaged(i32){};
1724 var list_lt = ArrayListUnmanaged(i32){};
1725 var list_gt = ArrayListUnmanaged(i32){};
1726
1727 try list_zero.appendSlice(a, &init);
1728 try list_eq.appendSlice(a, &init);
1729 try list_lt.appendSlice(a, &init);
1730 try list_gt.appendSlice(a, &init);
1731
1732 list_zero.replaceRangeAssumeCapacity(1, 0, &new);
1733 list_eq.replaceRangeAssumeCapacity(1, 3, &new);
1734 list_lt.replaceRangeAssumeCapacity(1, 2, &new);
1735
1736 // after_range > new_items.len in function body
1737 try testing.expect(1 + 4 > new.len);
1738 list_gt.replaceRangeAssumeCapacity(1, 4, &new);
1739
1740 try testing.expectEqualSlices(i32, list_zero.items, &result_zero);
1741 try testing.expectEqualSlices(i32, list_eq.items, &result_eq);
1742 try testing.expectEqualSlices(i32, list_lt.items, &result_le);
1743 try testing.expectEqualSlices(i32, list_gt.items, &result_gt);
1744 }
16681745}
16691746
16701747const Item = struct {