| ... | @@ -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 | } |
| 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 | /// 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 | } |
| 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 | test "fuzz against ArrayList oracle" { | 472 | test "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 | }, |