Skip to content

Remove elements from std.ArrayList while iterating over it #3037

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
181 changes: 171 additions & 10 deletions std/array_list.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std.zig");
const builtin = @import("builtin");
const debug = std.debug;
const assert = debug.assert;
const testing = std.testing;
Expand Down Expand Up @@ -201,27 +202,78 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
return self.pop();
}

// an iterator which allows you to remove the current element
// while iterating through it.
pub const Iterator = struct {
list: *const Self,
// how many items have we returned
count: usize,
list: *Self,

// index of the last element we returned, or null
// if we haven't called `next` yet.
cursor: ?usize = null,

// the last index we removed
removed_index: ?usize = null,

pub fn next(it: *Iterator) ?T {
if (it.count >= it.list.len) return null;
const val = it.list.at(it.count);
it.count += 1;
return val;
var idx: usize = 0;
if (it.cursor) |cursor| {
idx = cursor + 1;
}
if (idx >= it.list.len) return null;
it.cursor = idx;
return it.list.at(idx);
}

pub fn reset(it: *Iterator) void {
it.count = 0;
it.cursor = null;
it.removed_index = null;
}

pub fn swapRemove(it: *Iterator) T {
var cursor = it.cursor.?; // must call .next() at least once
if (it.removed_index) |ri| assert(ri != cursor); // removed the same element more than once
it.removed_index = cursor;
return it.list.swapRemove(cursor);
}

pub fn orderedRemove(it: *Iterator) T {
var cursor = it.cursor.?; // must call .next() at least once
if (it.removed_index) |ri| assert(ri != cursor); // removed the same element more than once
it.removed_index = cursor;
return it.list.orderedRemove(cursor);
}
};

pub fn iterator(self: *const Self) Iterator {
pub fn iterator(self: *Self) Iterator {
return Iterator{
.list = self,
.count = 0,
};
}

// an iterator that only allows you to iterate
// through the elements; you cannot remove the current
// element.
pub const IteratorConst = struct {
list: *const Self,

// index of the next element to return
next_index: usize = 0,

pub fn next(it: *IteratorConst) ?T {
if (it.next_index >= it.list.len) return null;
const val = it.list.at(it.next_index);
it.next_index += 1;
return val;
}

pub fn reset(it: *IteratorConst) void {
it.next_index = 0;
}
};

pub fn iteratorConst(self: *const Self) IteratorConst {
return IteratorConst{
.list = self,
};
}
};
Expand Down Expand Up @@ -409,6 +461,115 @@ test "std.ArrayList.iterator" {
testing.expect(it.next().? == 1);
}

test "std.ArrayList.iterator.swapRemove" {
var bytes: [1024]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;

var list = ArrayList(i32).init(allocator);
defer list.deinit();

try list.append(1);
try list.append(2);
try list.append(3);
try list.append(4);

{
var last_elem = list.at(list.len-1);
var itr = list.iterator();
_ = itr.next();
_ = itr.swapRemove();
testing.expectEqual(list.at(0), last_elem);
try list.insert(0, 1); // put the number back in.
}

var it = list.iterator();
while (it.next()) |next| {
if (next == 2) {
var removed = it.swapRemove();
testing.expect(removed == 2);
break;
}
}

it.reset();
testing.expect(it.next().? == 1);
testing.expect(it.next().? == 4);
testing.expect(it.next().? == 3);
testing.expect(it.next() == null);
}

test "std.ArrayList.iterator.orderedRemove" {
var bytes: [1024]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;

var list = ArrayList(i32).init(allocator);
defer list.deinit();

try list.append(1);
try list.append(2);
try list.append(3);
try list.append(4);

// check we can remove the first element
{
var second_elem = list.at(1);
var itr = list.iterator();
_ = itr.next();
_ = itr.orderedRemove();
testing.expectEqual(list.at(0), second_elem);
try list.insert(0, 1); // put the number back in.
}

var it = list.iterator();
while (it.next()) |next| {
if (next == 2) {
var removed = it.orderedRemove();
testing.expect(removed == 2);
break;
}
}

it.reset();
testing.expect(it.next().? == 1);
testing.expect(it.next().? == 3);
testing.expect(it.next().? == 4);
testing.expect(it.next() == null);
}

test "std.ArrayList.iteratorConst" {
var bytes: [1024]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;

var list = ArrayList(i32).init(allocator);
defer list.deinit();

try list.append(1);
try list.append(2);
try list.append(3);

const clist = list;

var count: i32 = 0;
var it = clist.iteratorConst();
while (it.next()) |next| {
testing.expect(next == count + 1);
count += 1;
}

testing.expect(count == 3);
testing.expect(it.next() == null);
it.reset();
count = 0;
while (it.next()) |next| {
testing.expect(next == count + 1);
count += 1;
if (count == 2) break;
}

it.reset();
testing.expect(it.next().? == 1);
}

test "std.ArrayList.insert" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
Expand Down
14 changes: 7 additions & 7 deletions std/http/headers.zig
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ pub const Headers = struct {
self.index.deinit();
}
{
var it = self.data.iterator();
var it = self.data.iteratorConst();
while (it.next()) |entry| {
entry.deinit();
}
Expand All @@ -146,7 +146,7 @@ pub const Headers = struct {
errdefer other.deinit();
try other.data.ensureCapacity(self.data.count());
try other.index.initCapacity(self.index.entries.len);
var it = self.data.iterator();
var it = self.data.iteratorConst();
while (it.next()) |entry| {
try other.append(entry.name, entry.value, entry.never_index);
}
Expand All @@ -157,10 +157,10 @@ pub const Headers = struct {
return self.data.count();
}

pub const Iterator = HeaderList.Iterator;
pub const Iterator = HeaderList.IteratorConst;

pub fn iterator(self: Self) Iterator {
return self.data.iterator();
return self.data.iteratorConst();
}

pub fn append(self: *Self, name: []const u8, value: []const u8, never_index: ?bool) !void {
Expand Down Expand Up @@ -290,7 +290,7 @@ pub const Headers = struct {
const dex = self.getIndices(name) orelse return null;

const buf = try allocator.alloc(HeaderEntry, dex.count());
var it = dex.iterator();
var it = dex.iteratorConst();
var n: usize = 0;
while (it.next()) |idx| {
buf[n] = self.data.at(idx);
Expand All @@ -315,7 +315,7 @@ pub const Headers = struct {
// adapted from mem.join
const total_len = blk: {
var sum: usize = dex.count() - 1; // space for separator(s)
var it = dex.iterator();
var it = dex.iteratorConst();
while (it.next()) |idx|
sum += self.data.at(idx).value.len;
break :blk sum;
Expand Down Expand Up @@ -351,7 +351,7 @@ pub const Headers = struct {
var it = self.data.iterator();
while (it.next()) |entry| {
var dex = &self.index.get(entry.name).?.value;
dex.appendAssumeCapacity(it.count);
dex.appendAssumeCapacity(it.cursor.?+1);
}
}
}
Expand Down