authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-12 21:46:19-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-12 21:46:19-08:00
log469bf6af07ad16fc5ac1421a302178a3fd508343
treef7c7b732bb115ae86d0fbffd073e89bdae2a6c52
parentf36d0573cd0f80b271b9f07a7215fa237d14ae9a
parent13e42b16cd91a92f18e9666e0ad8617b396c215a

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

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/31179

1 files changed, 216 insertions(+), 6 deletions(-)

lib/std/deque.zig+216-6
...@@ -174,18 +174,115 @@ pub fn Deque(comptime T: type) type {...@@ -174,18 +174,115 @@ pub fn Deque(comptime T: type) type {
174 deque.len += 1;174 deque.len += 1;
175 }175 }
176176
177 /// Add `items` to the front of the deque.
178 /// This is equivalent to iterating `items` in reverse and calling
179 /// `pushFront` on every single entry.
180 ///
181 /// Invalidates element pointers if additional memory is needed.
182 pub fn pushFrontSlice(deque: *Self, gpa: Allocator, items: []const T) error{OutOfMemory}!void {
183 try deque.ensureUnusedCapacity(gpa, items.len);
184 return deque.pushFrontSliceAssumeCapacity(items);
185 }
186
187 /// Add `items` to the front of the deque.
188 /// This is equivalent to iterating `items` in reverse and calling
189 /// `pushFront` on every single entry.
190 ///
191 /// Never invalidates element pointers.
192 ///
193 /// If the deque lacks unused capacity for the additional items, returns
194 /// `error.OutOfMemory`.
195 pub fn pushFrontSliceBounded(deque: *Self, items: []const T) error{OutOfMemory}!void {
196 if (deque.buffer.len - deque.len < items.len) return error.OutOfMemory;
197 return deque.pushFrontSliceAssumeCapacity(items);
198 }
199
200 /// Add `items` to the front of the deque.
201 /// This is equivalent to iterating `items` in reverse and calling
202 /// `pushFront` on every single entry.
203 ///
204 /// Never invalidates element pointers.
205 ///
206 /// Asserts that the deque can hold the additional items.
207 pub fn pushFrontSliceAssumeCapacity(deque: *Self, items: []const T) void {
208 assert(deque.buffer.len - deque.len >= items.len);
209 if (deque.head < items.len) {
210 @memcpy(deque.buffer[0..deque.head], items[items.len - deque.head ..]);
211 deque.head = deque.buffer.len - items.len + deque.head;
212 @memcpy(deque.buffer[deque.head..], items.ptr);
213 } else {
214 deque.head -= items.len;
215 @memcpy(deque.buffer[deque.head..][0..items.len], items);
216 }
217 deque.len += items.len;
218 }
219
220 /// Add `items` to the back of the deque.
221 /// This is equivalent to iterating `items` in order and calling
222 /// `pushBack` on every single entry.
223 ///
224 /// Invalidates element pointers if additional memory is needed.
225 pub fn pushBackSlice(deque: *Self, gpa: Allocator, items: []const T) error{OutOfMemory}!void {
226 try deque.ensureUnusedCapacity(gpa, items.len);
227 return deque.pushBackSliceAssumeCapacity(items);
228 }
229
230 /// Add `items` to the back of the deque.
231 /// This is equivalent to iterating `items` in order and calling
232 /// `pushBack` on every single entry.
233 ///
234 /// Never invalidates element pointers.
235 ///
236 /// If the deque lacks unused capacity for the additional items, returns
237 /// `error.OutOfMemory`.
238 pub fn pushBackSliceBounded(deque: *Self, items: []const T) error{OutOfMemory}!void {
239 if (deque.buffer.len - deque.len < items.len) return error.OutOfMemory;
240 return deque.pushBackSliceAssumeCapacity(items);
241 }
242
243 /// Add `items` to the back of the deque.
244 /// This is equivalent to iterating `items` in order and calling
245 /// `pushBack` on every single entry.
246 ///
247 /// Never invalidates element pointers.
248 ///
249 /// Asserts that the deque can hold the additional items.
250 pub fn pushBackSliceAssumeCapacity(deque: *Self, items: []const T) void {
251 assert(deque.buffer.len - deque.len >= items.len);
252 const trailing_buffer = deque.buffer[deque.bufferIndex(deque.len)..];
253 if (trailing_buffer.len < items.len) {
254 @memcpy(trailing_buffer, items[0..trailing_buffer.len]);
255 @memcpy(deque.buffer.ptr, items[trailing_buffer.len..]);
256 } else {
257 @memcpy(trailing_buffer[0..items.len], items);
258 }
259 deque.len += items.len;
260 }
261
177 /// Return the first item in the deque or null if empty.262 /// Return the first item in the deque or null if empty.
178 pub fn front(deque: *const Self) ?T {263 pub fn front(deque: *const Self) ?T {
179 if (deque.len == 0) return null;264 if (deque.len == 0) return null;
180 return deque.buffer[deque.head];265 return deque.buffer[deque.head];
181 }266 }
182267
268 /// Return pointer to the first item in the deque or null if empty.
269 pub fn frontPtr(deque: *const Self) ?*T {
270 if (deque.len == 0) return null;
271 return &deque.buffer[deque.head];
272 }
273
183 /// Return the last item in the deque or null if empty.274 /// Return the last item in the deque or null if empty.
184 pub fn back(deque: *const Self) ?T {275 pub fn back(deque: *const Self) ?T {
185 if (deque.len == 0) return null;276 if (deque.len == 0) return null;
186 return deque.buffer[deque.bufferIndex(deque.len - 1)];277 return deque.buffer[deque.bufferIndex(deque.len - 1)];
187 }278 }
188279
280 /// Return the last item in the deque or null if empty.
281 pub fn backPtr(deque: *const Self) ?*T {
282 if (deque.len == 0) return null;
283 return &deque.buffer[deque.bufferIndex(deque.len - 1)];
284 }
285
189 /// Return the item at the given index in the deque.286 /// Return the item at the given index in the deque.
190 ///287 ///
191 /// The first item in the queue is at index 0.288 /// The first item in the queue is at index 0.
...@@ -196,6 +293,16 @@ pub fn Deque(comptime T: type) type {...@@ -196,6 +293,16 @@ pub fn Deque(comptime T: type) type {
196 return deque.buffer[deque.bufferIndex(index)];293 return deque.buffer[deque.bufferIndex(index)];
197 }294 }
198295
296 /// Return pointer to the item at the given index in the deque.
297 ///
298 /// The first item in the queue is at index 0.
299 ///
300 /// Asserts that the index is in-bounds.
301 pub fn atPtr(deque: *const Self, index: usize) *T {
302 assert(index < deque.len);
303 return &deque.buffer[deque.bufferIndex(index)];
304 }
305
199 /// Remove and return the first item in the deque or null if empty.306 /// Remove and return the first item in the deque or null if empty.
200 pub fn popFront(deque: *Self) ?T {307 pub fn popFront(deque: *Self) ?T {
201 if (deque.len == 0) return null;308 if (deque.len == 0) return null;
...@@ -216,13 +323,24 @@ pub fn Deque(comptime T: type) type {...@@ -216,13 +323,24 @@ pub fn Deque(comptime T: type) type {
216 deque: *const Self,323 deque: *const Self,
217 index: usize,324 index: usize,
218325
326 pub fn peek(it: Iterator) ?T {
327 if (it.index >= it.deque.len) return null;
328 return it.deque.at(it.index);
329 }
219 pub fn next(it: *Iterator) ?T {330 pub fn next(it: *Iterator) ?T {
220 if (it.index < it.deque.len) {331 const item = it.peek() orelse return null;
221 defer it.index += 1;332 it.index += 1;
222 return it.deque.at(it.index);333 return item;
223 } else {334 }
224 return null;335
225 }336 pub fn peekPtr(it: Iterator) ?*T {
337 if (it.index >= it.deque.len) return null;
338 return it.deque.atPtr(it.index);
339 }
340 pub fn nextPtr(it: *Iterator) ?*T {
341 const item_ptr = it.peekPtr() orelse return null;
342 it.index += 1;
343 return item_ptr;
226 }344 }
227 };345 };
228346
...@@ -328,6 +446,74 @@ test "slow growth" {...@@ -328,6 +446,74 @@ test "slow growth" {
328 try testing.expectEqual(null, q.popBack());446 try testing.expectEqual(null, q.popBack());
329}447}
330448
449test "slice" {
450 const testing = std.testing;
451 const gpa = testing.allocator;
452
453 var q: Deque(i32) = .empty;
454 defer q.deinit(gpa);
455
456 try q.pushBackSlice(gpa, &.{ 3, 4, 5 });
457 try q.pushBackSlice(gpa, &.{ 6, 7 });
458 try q.pushFrontSlice(gpa, &.{2});
459 try q.pushBackSlice(gpa, &.{});
460 try q.pushFrontSlice(gpa, &.{ 0, 1 });
461 try q.pushFrontSlice(gpa, &.{});
462
463 try testing.expectEqual(0, q.popFront());
464 try testing.expectEqual(1, q.popFront());
465 try testing.expectEqual(7, q.popBack());
466 try testing.expectEqual(6, q.popBack());
467
468 try q.pushFrontSlice(gpa, &.{ 0, 1 });
469 try q.pushBackSlice(gpa, &.{ 6, 7 });
470
471 try testing.expectEqual(0, q.popFront());
472 try testing.expectEqual(1, q.popFront());
473 try testing.expectEqual(2, q.popFront());
474 try testing.expectEqual(7, q.popBack());
475 try testing.expectEqual(6, q.popBack());
476 try testing.expectEqual(3, q.popFront());
477 try testing.expectEqual(4, q.popFront());
478 try testing.expectEqual(5, q.popBack());
479 try testing.expectEqual(null, q.popFront());
480 try testing.expectEqual(null, q.popBack());
481}
482
483test "iterator" {
484 const testing = std.testing;
485 const gpa = testing.allocator;
486
487 var q: Deque(i32) = .empty;
488 defer q.deinit(gpa);
489
490 const items: []const i32 = &.{ 0, 1, 2, 3, 4, 5 };
491 try q.pushFrontSlice(gpa, items);
492
493 {
494 var it = q.iterator();
495 for (items) |item| {
496 try testing.expectEqual(item, it.peek());
497 try testing.expectEqual(item, it.next());
498 }
499 try testing.expectEqual(null, it.peek());
500 try testing.expectEqual(null, it.next());
501 }
502 {
503 var it = q.iterator();
504 for (items) |item| {
505 if (it.peekPtr()) |ptr| {
506 try testing.expectEqual(item, ptr.*);
507 } else return error.TestExpectedNonNull;
508 if (it.nextPtr()) |ptr| {
509 try testing.expectEqual(item, ptr.*);
510 } else return error.TestExpectedNonNull;
511 }
512 try testing.expectEqual(null, it.peekPtr());
513 try testing.expectEqual(null, it.nextPtr());
514 }
515}
516
331test "fuzz against ArrayList oracle" {517test "fuzz against ArrayList oracle" {
332 try std.testing.fuzz({}, fuzzAgainstArrayList, .{});518 try std.testing.fuzz({}, fuzzAgainstArrayList, .{});
333}519}
...@@ -362,6 +548,8 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {...@@ -362,6 +548,8 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {
362 const Action = enum {548 const Action = enum {
363 push_back,549 push_back,
364 push_front,550 push_front,
551 push_back_slice,
552 push_front_slice,
365 pop_back,553 pop_back,
366 pop_front,554 pop_front,
367 grow,555 grow,
...@@ -384,6 +572,28 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {...@@ -384,6 +572,28 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {
384 q.pushFrontBounded(item),572 q.pushFrontBounded(item),
385 );573 );
386 },574 },
575 .push_back_slice => {
576 var buffer: [std.math.maxInt(u3)]u32 = undefined;
577 const items = buffer[0..random.int(u3)];
578 for (items) |*item| {
579 item.* = random.int(u8);
580 }
581 try testing.expectEqual(
582 l.appendSliceBounded(items),
583 q.pushBackSliceBounded(items),
584 );
585 },
586 .push_front_slice => {
587 var buffer: [std.math.maxInt(u3)]u32 = undefined;
588 const items = buffer[0..random.int(u3)];
589 for (items) |*item| {
590 item.* = random.int(u8);
591 }
592 try testing.expectEqual(
593 l.insertSliceBounded(0, items),
594 q.pushFrontSliceBounded(items),
595 );
596 },
387 .pop_back => {597 .pop_back => {
388 try testing.expectEqual(l.pop(), q.popBack());598 try testing.expectEqual(l.pop(), q.popBack());
389 },599 },