commit 746dd51fafa8cdc8d98500af3b3b4b659c1aef92 (tree)
parent f4eecf8d7d4026a9e0ab8986b1c049ad2a0e01fc
Author: Hila Friedman <red.black.liquorice@gmail.com>
Date: Thu, 2 Apr 2026 15:11:24 +0200
Simplify `std.ArrayList.replaceRange` and `std.ArrayList.replaceRangeAssumeCapacity` (#31710)
The implementations of `std.ArrayList.replaceRange` and `std.ArrayList.replaceRangeAssumeCapacity` were needlessly complicated, called `@memcpy` too many times, and left the structure in a semi-modified state in OOM conditions. This commit fixes that.
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31710
Reviewed-by: Andrew Kelley <andrew@ziglang.org>
Co-authored-by: Hila Friedman <red.black.liquorice@gmail.com>
Co-committed-by: Hila Friedman <red.black.liquorice@gmail.com>
Diffstat:
1 file changed, 22 insertions(+), 31 deletions(-)
diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig
@@ -857,16 +857,8 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
len: usize,
new_items: []const T,
) Allocator.Error!void {
- const after_range = start + len;
- const range = self.items[start..after_range];
- if (range.len < new_items.len) {
- const first = new_items[0..range.len];
- const rest = new_items[range.len..];
- @memcpy(range[0..first.len], first);
- try self.insertSlice(gpa, after_range, rest);
- } else {
- self.replaceRangeAssumeCapacity(start, len, new_items);
- }
+ try self.ensureTotalCapacity(gpa, try addOrOom(self.items.len - len, new_items.len));
+ self.replaceRangeAssumeCapacity(start, len, new_items);
}
/// Grows or shrinks the list as necessary.
@@ -874,26 +866,20 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
/// Never invalidates element pointers.
///
/// Asserts the capacity is enough for additional items.
- pub fn replaceRangeAssumeCapacity(self: *Self, start: usize, len: usize, new_items: []const T) void {
- const after_range = start + len;
- const range = self.items[start..after_range];
-
- if (range.len == new_items.len)
- @memcpy(range[0..new_items.len], new_items)
- else if (range.len < new_items.len) {
- const first = new_items[0..range.len];
- const rest = new_items[range.len..];
- @memcpy(range[0..first.len], first);
- const dst = self.addManyAtAssumeCapacity(after_range, rest.len);
- @memcpy(dst, rest);
- } else {
- const extra = range.len - new_items.len;
- @memcpy(range[0..new_items.len], new_items);
- const src = self.items[after_range..];
- @memmove(self.items[after_range - extra ..][0..src.len], src);
- @memset(self.items[self.items.len - extra ..], undefined);
- self.items.len -= extra;
- }
+ pub fn replaceRangeAssumeCapacity(
+ self: *Self,
+ start: usize,
+ len: usize,
+ new_items: []const T,
+ ) void {
+ std.debug.assert(self.capacity - self.items.len >= new_items.len -| len);
+
+ const tail = self.items[start + len ..];
+ const vacated = self.items[self.items.len - (len -| new_items.len) ..];
+ self.items.len = self.items.len - len + new_items.len;
+ @memmove(self.items[start + new_items.len ..], tail);
+ @memcpy(self.items[start..][0..new_items.len], new_items);
+ @memset(vacated, undefined);
}
/// Grows or shrinks the list as necessary.
@@ -902,7 +888,12 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
///
/// If the unused capacity is insufficient for additional items,
/// returns `error.OutOfMemory`.
- pub fn replaceRangeBounded(self: *Self, start: usize, len: usize, new_items: []const T) error{OutOfMemory}!void {
+ pub fn replaceRangeBounded(
+ self: *Self,
+ start: usize,
+ len: usize,
+ new_items: []const T,
+ ) error{OutOfMemory}!void {
if (self.capacity - self.items.len < new_items.len -| len) return error.OutOfMemory;
return replaceRangeAssumeCapacity(self, start, len, new_items);
}