Merge branch std.Deque: add *Ptr and *Slice functions

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31179
This commit is contained in:
Andrew Kelley 2026-02-12 21:46:19 -08:00
commit 469bf6af07

View file

@ -174,18 +174,115 @@ pub fn Deque(comptime T: type) type {
deque.len += 1;
}
/// Add `items` to the front of the deque.
/// This is equivalent to iterating `items` in reverse and calling
/// `pushFront` on every single entry.
///
/// Invalidates element pointers if additional memory is needed.
pub fn pushFrontSlice(deque: *Self, gpa: Allocator, items: []const T) error{OutOfMemory}!void {
try deque.ensureUnusedCapacity(gpa, items.len);
return deque.pushFrontSliceAssumeCapacity(items);
}
/// Add `items` to the front of the deque.
/// This is equivalent to iterating `items` in reverse and calling
/// `pushFront` on every single entry.
///
/// Never invalidates element pointers.
///
/// If the deque lacks unused capacity for the additional items, returns
/// `error.OutOfMemory`.
pub fn pushFrontSliceBounded(deque: *Self, items: []const T) error{OutOfMemory}!void {
if (deque.buffer.len - deque.len < items.len) return error.OutOfMemory;
return deque.pushFrontSliceAssumeCapacity(items);
}
/// Add `items` to the front of the deque.
/// This is equivalent to iterating `items` in reverse and calling
/// `pushFront` on every single entry.
///
/// Never invalidates element pointers.
///
/// Asserts that the deque can hold the additional items.
pub fn pushFrontSliceAssumeCapacity(deque: *Self, items: []const T) void {
assert(deque.buffer.len - deque.len >= items.len);
if (deque.head < items.len) {
@memcpy(deque.buffer[0..deque.head], items[items.len - deque.head ..]);
deque.head = deque.buffer.len - items.len + deque.head;
@memcpy(deque.buffer[deque.head..], items.ptr);
} else {
deque.head -= items.len;
@memcpy(deque.buffer[deque.head..][0..items.len], items);
}
deque.len += items.len;
}
/// Add `items` to the back of the deque.
/// This is equivalent to iterating `items` in order and calling
/// `pushBack` on every single entry.
///
/// Invalidates element pointers if additional memory is needed.
pub fn pushBackSlice(deque: *Self, gpa: Allocator, items: []const T) error{OutOfMemory}!void {
try deque.ensureUnusedCapacity(gpa, items.len);
return deque.pushBackSliceAssumeCapacity(items);
}
/// Add `items` to the back of the deque.
/// This is equivalent to iterating `items` in order and calling
/// `pushBack` on every single entry.
///
/// Never invalidates element pointers.
///
/// If the deque lacks unused capacity for the additional items, returns
/// `error.OutOfMemory`.
pub fn pushBackSliceBounded(deque: *Self, items: []const T) error{OutOfMemory}!void {
if (deque.buffer.len - deque.len < items.len) return error.OutOfMemory;
return deque.pushBackSliceAssumeCapacity(items);
}
/// Add `items` to the back of the deque.
/// This is equivalent to iterating `items` in order and calling
/// `pushBack` on every single entry.
///
/// Never invalidates element pointers.
///
/// Asserts that the deque can hold the additional items.
pub fn pushBackSliceAssumeCapacity(deque: *Self, items: []const T) void {
assert(deque.buffer.len - deque.len >= items.len);
const trailing_buffer = deque.buffer[deque.bufferIndex(deque.len)..];
if (trailing_buffer.len < items.len) {
@memcpy(trailing_buffer, items[0..trailing_buffer.len]);
@memcpy(deque.buffer.ptr, items[trailing_buffer.len..]);
} else {
@memcpy(trailing_buffer[0..items.len], items);
}
deque.len += items.len;
}
/// Return the first item in the deque or null if empty.
pub fn front(deque: *const Self) ?T {
if (deque.len == 0) return null;
return deque.buffer[deque.head];
}
/// Return pointer to the first item in the deque or null if empty.
pub fn frontPtr(deque: *const Self) ?*T {
if (deque.len == 0) return null;
return &deque.buffer[deque.head];
}
/// Return the last item in the deque or null if empty.
pub fn back(deque: *const Self) ?T {
if (deque.len == 0) return null;
return deque.buffer[deque.bufferIndex(deque.len - 1)];
}
/// Return the last item in the deque or null if empty.
pub fn backPtr(deque: *const Self) ?*T {
if (deque.len == 0) return null;
return &deque.buffer[deque.bufferIndex(deque.len - 1)];
}
/// Return the item at the given index in the deque.
///
/// The first item in the queue is at index 0.
@ -196,6 +293,16 @@ pub fn Deque(comptime T: type) type {
return deque.buffer[deque.bufferIndex(index)];
}
/// Return pointer to the item at the given index in the deque.
///
/// The first item in the queue is at index 0.
///
/// Asserts that the index is in-bounds.
pub fn atPtr(deque: *const Self, index: usize) *T {
assert(index < deque.len);
return &deque.buffer[deque.bufferIndex(index)];
}
/// Remove and return the first item in the deque or null if empty.
pub fn popFront(deque: *Self) ?T {
if (deque.len == 0) return null;
@ -216,13 +323,24 @@ pub fn Deque(comptime T: type) type {
deque: *const Self,
index: usize,
pub fn peek(it: Iterator) ?T {
if (it.index >= it.deque.len) return null;
return it.deque.at(it.index);
}
pub fn next(it: *Iterator) ?T {
if (it.index < it.deque.len) {
defer it.index += 1;
return it.deque.at(it.index);
} else {
return null;
}
const item = it.peek() orelse return null;
it.index += 1;
return item;
}
pub fn peekPtr(it: Iterator) ?*T {
if (it.index >= it.deque.len) return null;
return it.deque.atPtr(it.index);
}
pub fn nextPtr(it: *Iterator) ?*T {
const item_ptr = it.peekPtr() orelse return null;
it.index += 1;
return item_ptr;
}
};
@ -328,6 +446,74 @@ test "slow growth" {
try testing.expectEqual(null, q.popBack());
}
test "slice" {
const testing = std.testing;
const gpa = testing.allocator;
var q: Deque(i32) = .empty;
defer q.deinit(gpa);
try q.pushBackSlice(gpa, &.{ 3, 4, 5 });
try q.pushBackSlice(gpa, &.{ 6, 7 });
try q.pushFrontSlice(gpa, &.{2});
try q.pushBackSlice(gpa, &.{});
try q.pushFrontSlice(gpa, &.{ 0, 1 });
try q.pushFrontSlice(gpa, &.{});
try testing.expectEqual(0, q.popFront());
try testing.expectEqual(1, q.popFront());
try testing.expectEqual(7, q.popBack());
try testing.expectEqual(6, q.popBack());
try q.pushFrontSlice(gpa, &.{ 0, 1 });
try q.pushBackSlice(gpa, &.{ 6, 7 });
try testing.expectEqual(0, q.popFront());
try testing.expectEqual(1, q.popFront());
try testing.expectEqual(2, q.popFront());
try testing.expectEqual(7, q.popBack());
try testing.expectEqual(6, q.popBack());
try testing.expectEqual(3, q.popFront());
try testing.expectEqual(4, q.popFront());
try testing.expectEqual(5, q.popBack());
try testing.expectEqual(null, q.popFront());
try testing.expectEqual(null, q.popBack());
}
test "iterator" {
const testing = std.testing;
const gpa = testing.allocator;
var q: Deque(i32) = .empty;
defer q.deinit(gpa);
const items: []const i32 = &.{ 0, 1, 2, 3, 4, 5 };
try q.pushFrontSlice(gpa, items);
{
var it = q.iterator();
for (items) |item| {
try testing.expectEqual(item, it.peek());
try testing.expectEqual(item, it.next());
}
try testing.expectEqual(null, it.peek());
try testing.expectEqual(null, it.next());
}
{
var it = q.iterator();
for (items) |item| {
if (it.peekPtr()) |ptr| {
try testing.expectEqual(item, ptr.*);
} else return error.TestExpectedNonNull;
if (it.nextPtr()) |ptr| {
try testing.expectEqual(item, ptr.*);
} else return error.TestExpectedNonNull;
}
try testing.expectEqual(null, it.peekPtr());
try testing.expectEqual(null, it.nextPtr());
}
}
test "fuzz against ArrayList oracle" {
try std.testing.fuzz({}, fuzzAgainstArrayList, .{});
}
@ -362,6 +548,8 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {
const Action = enum {
push_back,
push_front,
push_back_slice,
push_front_slice,
pop_back,
pop_front,
grow,
@ -384,6 +572,28 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {
q.pushFrontBounded(item),
);
},
.push_back_slice => {
var buffer: [std.math.maxInt(u3)]u32 = undefined;
const items = buffer[0..random.int(u3)];
for (items) |*item| {
item.* = random.int(u8);
}
try testing.expectEqual(
l.appendSliceBounded(items),
q.pushBackSliceBounded(items),
);
},
.push_front_slice => {
var buffer: [std.math.maxInt(u3)]u32 = undefined;
const items = buffer[0..random.int(u3)];
for (items) |*item| {
item.* = random.int(u8);
}
try testing.expectEqual(
l.insertSliceBounded(0, items),
q.pushFrontSliceBounded(items),
);
},
.pop_back => {
try testing.expectEqual(l.pop(), q.popBack());
},