authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-02-11 00:57:19+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-02-12 21:45:13-08:00
log527e97b25201a1a252bcff6d79198513babda2db
tree86a2f27dd83e39f16d4baabc98f6158c302cd170
parent35c5611f0707a46f6314a7b4e4597ff56bfead18

std.Deque: add `*Slice` variants of push functions

This mirrors the `*Slice` variants e.g. `std.ArrayList` already provides.

1 files changed, 143 insertions(+), 0 deletions(-)

lib/std/deque.zig+143
...@@ -174,6 +174,91 @@ pub fn Deque(comptime T: type) type {...@@ -174,6 +174,91 @@ 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;
...@@ -350,6 +435,40 @@ test "slow growth" {...@@ -350,6 +435,40 @@ test "slow growth" {
350 try testing.expectEqual(null, q.popBack());435 try testing.expectEqual(null, q.popBack());
351}436}
352437
438test "slice" {
439 const testing = std.testing;
440 const gpa = testing.allocator;
441
442 var q: Deque(i32) = .empty;
443 defer q.deinit(gpa);
444
445 try q.pushBackSlice(gpa, &.{ 3, 4, 5 });
446 try q.pushBackSlice(gpa, &.{ 6, 7 });
447 try q.pushFrontSlice(gpa, &.{2});
448 try q.pushBackSlice(gpa, &.{});
449 try q.pushFrontSlice(gpa, &.{ 0, 1 });
450 try q.pushFrontSlice(gpa, &.{});
451
452 try testing.expectEqual(0, q.popFront());
453 try testing.expectEqual(1, q.popFront());
454 try testing.expectEqual(7, q.popBack());
455 try testing.expectEqual(6, q.popBack());
456
457 try q.pushFrontSlice(gpa, &.{ 0, 1 });
458 try q.pushBackSlice(gpa, &.{ 6, 7 });
459
460 try testing.expectEqual(0, q.popFront());
461 try testing.expectEqual(1, q.popFront());
462 try testing.expectEqual(2, q.popFront());
463 try testing.expectEqual(7, q.popBack());
464 try testing.expectEqual(6, q.popBack());
465 try testing.expectEqual(3, q.popFront());
466 try testing.expectEqual(4, q.popFront());
467 try testing.expectEqual(5, q.popBack());
468 try testing.expectEqual(null, q.popFront());
469 try testing.expectEqual(null, q.popBack());
470}
471
353test "fuzz against ArrayList oracle" {472test "fuzz against ArrayList oracle" {
354 try std.testing.fuzz({}, fuzzAgainstArrayList, .{});473 try std.testing.fuzz({}, fuzzAgainstArrayList, .{});
355}474}
...@@ -384,6 +503,8 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {...@@ -384,6 +503,8 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {
384 const Action = enum {503 const Action = enum {
385 push_back,504 push_back,
386 push_front,505 push_front,
506 push_back_slice,
507 push_front_slice,
387 pop_back,508 pop_back,
388 pop_front,509 pop_front,
389 grow,510 grow,
...@@ -406,6 +527,28 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {...@@ -406,6 +527,28 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void {
406 q.pushFrontBounded(item),527 q.pushFrontBounded(item),
407 );528 );
408 },529 },
530 .push_back_slice => {
531 var buffer: [std.math.maxInt(u3)]u32 = undefined;
532 const items = buffer[0..random.int(u3)];
533 for (items) |*item| {
534 item.* = random.int(u8);
535 }
536 try testing.expectEqual(
537 l.appendSliceBounded(items),
538 q.pushBackSliceBounded(items),
539 );
540 },
541 .push_front_slice => {
542 var buffer: [std.math.maxInt(u3)]u32 = undefined;
543 const items = buffer[0..random.int(u3)];
544 for (items) |*item| {
545 item.* = random.int(u8);
546 }
547 try testing.expectEqual(
548 l.insertSliceBounded(0, items),
549 q.pushFrontSliceBounded(items),
550 );
551 },
409 .pop_back => {552 .pop_back => {
410 try testing.expectEqual(l.pop(), q.popBack());553 try testing.expectEqual(l.pop(), q.popBack());
411 },554 },