| ... | ... | @@ -174,6 +174,91 @@ pub fn Deque(comptime T: type) type { |
| 174 | 174 | deque.len += 1; |
| 175 | 175 | } |
| 176 | 176 | |
| 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 | 262 | /// Return the first item in the deque or null if empty. |
| 178 | 263 | pub fn front(deque: *const Self) ?T { |
| 179 | 264 | if (deque.len == 0) return null; |
| ... | ... | @@ -350,6 +435,40 @@ test "slow growth" { |
| 350 | 435 | try testing.expectEqual(null, q.popBack()); |
| 351 | 436 | } |
| 352 | 437 | |
| 438 | test "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 | |
| 353 | 472 | test "fuzz against ArrayList oracle" { |
| 354 | 473 | try std.testing.fuzz({}, fuzzAgainstArrayList, .{}); |
| 355 | 474 | } |
| ... | ... | @@ -384,6 +503,8 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void { |
| 384 | 503 | const Action = enum { |
| 385 | 504 | push_back, |
| 386 | 505 | push_front, |
| 506 | push_back_slice, |
| 507 | push_front_slice, |
| 387 | 508 | pop_back, |
| 388 | 509 | pop_front, |
| 389 | 510 | grow, |
| ... | ... | @@ -406,6 +527,28 @@ fn fuzzAgainstArrayList(_: void, input: []const u8) anyerror!void { |
| 406 | 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 | 552 | .pop_back => { |
| 410 | 553 | try testing.expectEqual(l.pop(), q.popBack()); |
| 411 | 554 | }, |