1 module mecca.containers.lists; 2 3 // Licensed under the Boost license. Full copyright information in the AUTHORS file 4 5 import std.traits; 6 7 import mecca.lib.memory: prefetch; 8 import mecca.lib.exception; 9 import mecca.log; 10 11 12 /////////////////////////////////////////////////////////////////////////////////////////////////// 13 // 14 // LinkedList: doubly-linked list of elements; next/prev stored in-element (intrusive) 15 // 16 /////////////////////////////////////////////////////////////////////////////////////////////////// 17 18 struct _LinkedList(T, string nextAttr, string prevAttr, string ownerAttr, bool withLength) { 19 T head; 20 static if (withLength) { 21 size_t length; 22 } 23 enum withOwner = ownerAttr.length > 0; 24 25 private enum MAY_PREFETCH = __traits(compiles, {T t; prefetch(t);}); 26 27 static if(isPointer!T) pure nothrow @nogc { 28 private static bool isValid(const T t) pure nothrow @safe @nogc { 29 return t !is null; 30 } 31 32 enum invalid = null; 33 } else { 34 private static bool isValid(const T t) pure nothrow @safe @nogc { 35 return t.isValid; 36 } 37 38 enum invalid = T.invalid; 39 } 40 41 static assert (!isValid(invalid), "invalid is valid for type " ~ T.stringof); 42 43 static T getNextOf(T node) nothrow @safe @nogc { 44 pragma(inline, true); 45 T tmp = mixin("node." ~ nextAttr); 46 static if(MAY_PREFETCH) prefetch(tmp); 47 return tmp; 48 } 49 static void setNextOf(T node, T val) nothrow @safe @nogc { 50 pragma(inline, true); 51 mixin("node." ~ nextAttr ~ " = val;"); 52 } 53 54 static T getPrevOf(T node) nothrow @safe @nogc { 55 pragma(inline, true); 56 T tmp = mixin("node." ~ prevAttr); 57 static if (MAY_PREFETCH) prefetch(tmp); 58 return tmp; 59 } 60 static void setPrevOf(T node, T val) nothrow @safe @nogc { 61 pragma(inline, true); 62 mixin("node." ~ prevAttr ~ " = val;"); 63 } 64 65 static if (withOwner) { 66 @notrace private static _LinkedList* getOwnerOf(T node) nothrow @trusted @nogc { 67 pragma(inline, true); 68 mixin("return cast(_LinkedList*)(node." ~ ownerAttr ~ ");"); 69 } 70 @notrace private static void clearOwnerOf(T node) nothrow @safe @nogc { 71 pragma(inline, true); 72 mixin("node." ~ ownerAttr ~ " = null;"); 73 } 74 } 75 76 @property bool empty() const pure nothrow { 77 static if (withLength) assert (isValid(head) || length == 0, "head is null but length != 0"); 78 return !isValid(head); 79 } 80 @property T tail() nothrow { 81 static if (withLength) assert (isValid(head) || length == 0, "head is null but length != 0"); 82 return isValid(head) ? getPrevOf(head) : invalid; 83 } 84 85 bool _insert(bool after)(T anchor, T node) nothrow @safe @nogc { 86 assert (isValid(node), "appending null"); 87 88 static if (withOwner) { 89 assert (!isValid(anchor) || getOwnerOf(anchor) is &this, "anchor does not belong to this list"); 90 auto owner = getOwnerOf(node); 91 if (owner is &this) { 92 return false; 93 } 94 assert (owner is null, "owner already set"); 95 mixin("node." ~ ownerAttr ~ " = &this;"); 96 } 97 ASSERT!"next is linked"(!isValid(getNextOf(node))); 98 ASSERT!"prev is linked"(!isValid(getPrevOf(node))); 99 100 if (!isValid(head)) { 101 assert (!isValid(anchor)); 102 static if (withLength) assert (length == 0); 103 head = node; 104 setNextOf(node, node); 105 setPrevOf(node, node); 106 } 107 else { 108 static if (withLength) assert (length > 0, "List with head has length 0"); 109 static if (after) { 110 auto next = getNextOf(anchor); 111 setNextOf(anchor, node); 112 setPrevOf(node, anchor); 113 setNextOf(node, next); 114 setPrevOf(next, node); 115 } 116 else { 117 auto prev = getPrevOf(anchor); 118 setNextOf(node, anchor); 119 setPrevOf(node, prev); 120 setPrevOf(anchor, node); 121 setNextOf(prev, node); 122 123 if (anchor is head) { 124 head = node; 125 } 126 } 127 } 128 129 static if (withLength) length++; 130 return true; 131 } 132 133 bool insertAfter(T anchor, T node) nothrow @safe @nogc {pragma(inline, true); 134 return _insert!true(anchor, node); 135 } 136 bool insertBefore(T anchor, T node) nothrow @safe @nogc {pragma(inline, true); 137 return _insert!false(anchor, node); 138 } 139 140 bool append(T node) nothrow @safe @nogc {pragma(inline, true); 141 return insertAfter(tail, node); 142 } 143 bool prepend(T node) nothrow @safe @nogc {pragma(inline, true); 144 return insertBefore(head, node); 145 } 146 147 bool remove(T node) nothrow @safe @nogc { 148 assert (isValid(node)); 149 assert (!empty); 150 151 static if (withOwner) { 152 auto owner = getOwnerOf(node); 153 if (owner is null) { 154 assert (!isValid(getNextOf(node)), "no owner but next is linked"); 155 assert (!isValid(getPrevOf(node)), "no owner but prev is linked"); 156 return false; 157 } 158 ASSERT!"Trying to remove node that doesn't belong to list. Owner %s, this %s" (owner is &this, owner, &this); 159 clearOwnerOf(node); 160 } 161 162 if (getNextOf(head) is head) { 163 // single element 164 assert (node is head); 165 setNextOf(node, invalid); 166 setPrevOf(node, invalid); 167 head = invalid; 168 } 169 else { 170 auto p = getPrevOf(node); 171 auto n = getNextOf(node); 172 setNextOf(p, n); 173 setPrevOf(n, p); 174 if (node is head) { 175 head = n; 176 } 177 } 178 setNextOf(node, invalid); 179 setPrevOf(node, invalid); 180 181 static if (withLength) { 182 assert (length > 0); 183 length--; 184 } 185 return true; 186 } 187 188 static if (withOwner) { 189 bool opBinaryRight(string op: "in")(T node) const nothrow @safe @nogc { 190 return getOwnerOf(node) is &this; 191 } 192 193 static bool discard(T node) nothrow @safe @nogc { 194 if (auto owner = getOwnerOf(node)) { 195 return owner.remove(node); 196 } 197 else { 198 assert (!isValid(getNextOf(node)), "no owner but next is linked"); 199 assert (!isValid(getPrevOf(node)), "no owner but prev is linked"); 200 return false; 201 } 202 } 203 } 204 205 T popHead() nothrow @safe @nogc { 206 auto node = head; 207 if (isValid(node)) { 208 remove(node); 209 } 210 return node; 211 } 212 T popTail() nothrow @safe @nogc { 213 auto node = tail; 214 if (isValid(node)) { 215 remove(node); 216 } 217 return node; 218 } 219 220 static if (!withOwner) { 221 void splice(_LinkedList* second) nothrow { 222 assert (second !is &this); 223 if (second.empty) { 224 return; 225 } 226 if (empty) { 227 head = second.head; 228 } 229 else { 230 setNextOf(tail, second.head); 231 setPrevOf(second.head, tail); 232 setNextOf(second.tail, head); 233 setPrevOf(head, second.tail); 234 } 235 236 // For safety reasons, `second` is emptied (otherwise pop() from it would break this) 237 second.head = invalid; 238 static if (withLength) { 239 length += second.length; 240 second.length = 0; 241 } 242 } 243 } 244 245 void removeAll() nothrow { 246 while (isValid(head)) { 247 auto next = getNextOf(head); 248 setPrevOf(head, invalid); 249 setNextOf(head, invalid); 250 static if (withOwner) clearOwnerOf(head); 251 head = (next is head) ? invalid : next; 252 } 253 static if (withLength) length = 0; 254 } 255 256 static struct Range { 257 _LinkedList* list; 258 T front; 259 260 @property bool empty() const pure nothrow @nogc { 261 return !isValid(front); 262 } 263 void popFront() nothrow { 264 assert (list !is null); 265 assert (isValid(front)); 266 front = getNextOf(front); 267 if (front is list.head) { 268 front = invalid; 269 } 270 } 271 } 272 @property auto range() nothrow { 273 return Range(&this, head); 274 } 275 276 static struct ReverseRange { 277 _LinkedList* list; 278 T front; 279 280 @property bool empty() const pure nothrow @nogc { 281 return !isValid(front); 282 } 283 void popFront() nothrow { 284 assert (list); 285 assert (isValid(front)); 286 front = getPrevOf(front); 287 if (front is list.tail) { 288 front = invalid; 289 } 290 } 291 } 292 @property auto reverseRange() nothrow { 293 return ReverseRange(&this, tail); 294 } 295 } 296 297 alias LinkedList(T, string nextAttr="_next", string prevAttr="_prev") = _LinkedList!(T, nextAttr, prevAttr, "", false); 298 alias LinkedListWithLength(T, string nextAttr="_next", string prevAttr="_prev") = _LinkedList!(T, nextAttr, prevAttr, "", true); 299 300 alias LinkedListWithOwner(T, string nextAttr="_next", string prevAttr="_prev", string ownerAttr="_owner") = _LinkedList!(T, nextAttr, prevAttr, ownerAttr, false); 301 alias LinkedListWithLengthAndOwner(T, string nextAttr="_next", string prevAttr="_prev", string ownerAttr="_owner") = _LinkedList!(T, nextAttr, prevAttr, ownerAttr, true); 302 303 304 unittest { 305 import std.stdio; 306 import std..string; 307 308 struct Node { 309 int value; 310 Node* _next; 311 Node* _prev; 312 313 @disable this(this); 314 } 315 316 Node[10] nodes; 317 foreach(int i, ref n; nodes) { 318 n.value = i; 319 } 320 321 LinkedList!(Node*) list; 322 assert (list.head is null); 323 324 list.append(&nodes[0]); 325 assert (list.head.value == 0); 326 327 list.append(&nodes[1]); 328 list.append(&nodes[2]); 329 list.append(&nodes[3]); 330 list.append(&nodes[4]); 331 list.append(&nodes[5]); 332 list.append(&nodes[6]); 333 list.append(&nodes[7]); 334 list.append(&nodes[8]); 335 list.append(&nodes[9]); 336 assert (list.head.value == 0); 337 338 void matchElements(R)(R range, int[] expected) { 339 int[] arr; 340 foreach(n; range) { 341 arr ~= n.value; 342 } 343 //writeln(arr); 344 assert (arr == expected, "%s != %s".format(arr, expected)); 345 } 346 347 matchElements(list.range, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 348 matchElements(list.range, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 349 350 list.remove(&nodes[3]); 351 matchElements(list.range, [0, 1, 2, 4, 5, 6, 7, 8, 9]); 352 353 list.remove(&nodes[0]); 354 matchElements(list.range, [1, 2, 4, 5, 6, 7, 8, 9]); 355 356 list.remove(&nodes[9]); 357 matchElements(list.range, [1, 2, 4, 5, 6, 7, 8]); 358 359 list.insertAfter(&nodes[2], &nodes[3]); 360 matchElements(list.range, [1, 2, 3, 4, 5, 6, 7, 8]); 361 362 list.insertBefore(&nodes[7], &nodes[9]); 363 matchElements(list.range, [1, 2, 3, 4, 5, 6, 9, 7, 8]); 364 365 matchElements(list.reverseRange, [8, 7, 9, 6, 5, 4, 3, 2, 1]); 366 367 list.removeAll(); 368 assert (list.empty); 369 } 370 371 unittest { 372 import std.stdio; 373 import std..string; 374 375 struct Node { 376 int value; 377 Node* _next; 378 Node* _prev; 379 380 @disable this(this); 381 } 382 383 Node[10] nodes; 384 foreach(int i, ref n; nodes) { 385 n.value = i; 386 } 387 388 LinkedListWithLength!(Node*) list; 389 assert (list.head is null); 390 391 list.append(&nodes[0]); 392 assert (list.head.value == 0); 393 394 list.append(&nodes[1]); 395 list.append(&nodes[2]); 396 list.append(&nodes[3]); 397 list.append(&nodes[4]); 398 list.append(&nodes[5]); 399 list.append(&nodes[6]); 400 list.append(&nodes[7]); 401 list.append(&nodes[8]); 402 list.append(&nodes[9]); 403 assert (list.head.value == 0); 404 405 void matchElements(R)(R range, int[] expected) { 406 int[] arr; 407 foreach(n; range) { 408 arr ~= n.value; 409 } 410 //writeln(arr); 411 assert (arr == expected, "%s != %s".format(arr, expected)); 412 } 413 414 matchElements(list.range, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 415 matchElements(list.range, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 416 417 list.remove(&nodes[3]); 418 matchElements(list.range, [0, 1, 2, 4, 5, 6, 7, 8, 9]); 419 420 list.remove(&nodes[0]); 421 matchElements(list.range, [1, 2, 4, 5, 6, 7, 8, 9]); 422 423 list.remove(&nodes[9]); 424 matchElements(list.range, [1, 2, 4, 5, 6, 7, 8]); 425 426 list.insertAfter(&nodes[2], &nodes[3]); 427 matchElements(list.range, [1, 2, 3, 4, 5, 6, 7, 8]); 428 429 list.insertBefore(&nodes[7], &nodes[9]); 430 matchElements(list.range, [1, 2, 3, 4, 5, 6, 9, 7, 8]); 431 432 matchElements(list.reverseRange, [8, 7, 9, 6, 5, 4, 3, 2, 1]); 433 434 list.removeAll(); 435 assert (list.empty); 436 } 437 438 unittest { 439 import std.stdio; 440 import std..string; 441 442 // Linked list using abstracted _next and _prev 443 struct Node { 444 @disable this(this); 445 446 static Node[10] theNodes; 447 448 int value; 449 ubyte nextIdx = ubyte.max; 450 ubyte prevIdx = ubyte.max; 451 452 @property Node* _next() nothrow @safe @nogc { 453 return nextIdx == ubyte.max ? null : &theNodes[nextIdx]; 454 } 455 @property void _next(Node* n) nothrow @safe @nogc { 456 nextIdx = n is null ? ubyte.max : cast(ubyte)(n - &theNodes[0]); 457 } 458 459 @property Node* _prev() nothrow @safe @nogc { 460 return prevIdx == ubyte.max ? null : &theNodes[prevIdx]; 461 } 462 @property void _prev(Node* n) nothrow @safe @nogc { 463 prevIdx = n is null ? ubyte.max : cast(ubyte)(n - &theNodes[0]); 464 } 465 } 466 467 foreach(int i, ref n; Node.theNodes) { 468 n.value = 100 + i; 469 } 470 471 LinkedList!(Node*) list; 472 assert (list.head is null); 473 474 list.append(&Node.theNodes[0]); 475 assert (list.head.value == 100); 476 477 list.append(&Node.theNodes[1]); 478 list.append(&Node.theNodes[2]); 479 list.append(&Node.theNodes[3]); 480 list.append(&Node.theNodes[4]); 481 list.append(&Node.theNodes[5]); 482 list.append(&Node.theNodes[6]); 483 list.append(&Node.theNodes[7]); 484 list.append(&Node.theNodes[8]); 485 list.append(&Node.theNodes[9]); 486 assert (list.head.value == 100); 487 488 list.remove(&Node.theNodes[5]); 489 list.remove(&Node.theNodes[6]); 490 491 int[] arr; 492 foreach(n; list.range) { 493 arr ~= n.value; 494 } 495 assert(arr == [100, 101, 102, 103, 104, 107, 108, 109]); 496 } 497 498 unittest { 499 import std.stdio; 500 import std..string; 501 502 // Linked list using abstracted pointer 503 struct Node { 504 static Node[10] theNodes; 505 506 static struct Ptr { 507 ubyte index = ubyte.max; 508 509 @property Node* node() nothrow @safe @nogc { 510 if (index==ubyte.max) 511 return null; 512 513 return &theNodes[index]; 514 } 515 516 @property ref Ptr _prev() nothrow @safe @nogc { 517 return node._prev; 518 } 519 520 @property ref Ptr _next() nothrow @safe @nogc { 521 return node._next; 522 } 523 524 bool isValid() const pure nothrow @safe @nogc { 525 return index != ubyte.max; 526 } 527 528 enum invalid = Ptr.init; 529 } 530 531 int value; 532 Ptr _next; 533 Ptr _prev; 534 } 535 536 foreach(int i, ref n; Node.theNodes) { 537 n.value = 100 + i; 538 } 539 540 LinkedList!(Node.Ptr) list; 541 assert (!list.head.isValid); 542 543 list.append(Node.Ptr(0)); 544 assert (list.head.node.value == 100); 545 546 list.append(Node.Ptr(1)); 547 list.append(Node.Ptr(2)); 548 list.append(Node.Ptr(3)); 549 list.append(Node.Ptr(4)); 550 list.append(Node.Ptr(5)); 551 list.append(Node.Ptr(6)); 552 list.append(Node.Ptr(7)); 553 list.append(Node.Ptr(8)); 554 list.append(Node.Ptr(9)); 555 assert (list.head.node.value == 100); 556 557 list.remove(Node.Ptr(5)); 558 list.remove(Node.Ptr(6)); 559 560 int[] arr; 561 foreach(n; list.range) { 562 arr ~= n.node.value; 563 } 564 assert(arr == [100, 101, 102, 103, 104, 107, 108, 109], "Incorrect result: %s".format(arr)); 565 } 566 567 unittest { 568 import std.stdio; 569 import std..string; 570 571 struct Node { 572 int value; 573 Node* _next; 574 Node* _prev; 575 void* _owner; 576 577 @disable this(this); 578 } 579 580 Node[10] nodes; 581 foreach(int i, ref n; nodes) { 582 n.value = i; 583 } 584 585 LinkedListWithOwner!(Node*) set; 586 assert (set.empty); 587 588 assert(set.append(&nodes[1])); 589 assert(set.append(&nodes[2])); 590 assert(set.append(&nodes[3])); 591 assert(!set.append(&nodes[2])); 592 593 assert (&nodes[1] in set); 594 assert (&nodes[2] in set); 595 assert (&nodes[3] in set); 596 assert (&nodes[4] !in set); 597 598 assert (set.remove(&nodes[2])); 599 assert (&nodes[2] !in set); 600 601 assert (!set.remove(&nodes[2])); 602 assert (!set.discard(&nodes[2])); 603 assert (set.discard(&nodes[1])); 604 assert (&nodes[1] !in set); 605 } 606 607 /////////////////////////////////////////////////////////////////////////////////////////////////// 608 // 609 // LinkedQueue: singly-linked list of elements; next pointer is stored in-element (intrusive). 610 // insert in the head or tail, from only from head 611 // 612 /////////////////////////////////////////////////////////////////////////////////////////////////// 613 614 struct _LinkedQueue(T, string nextAttr, bool withLength) { 615 T head; 616 T tail; 617 static if (withLength) { 618 size_t length; 619 } 620 621 static T getNextOf(T node) nothrow @safe @nogc { 622 pragma(inline, true); 623 T tmp = mixin("node." ~ nextAttr); 624 prefetch(tmp); 625 return tmp; 626 } 627 static void setNextOf(T node, T val) nothrow @safe @nogc { 628 pragma(inline, true); 629 mixin("node." ~ nextAttr ~ " = val;"); 630 } 631 632 @property empty() const pure nothrow @safe @nogc { 633 static if (withLength) { 634 assert ((head is null && tail is null && length == 0) || (head !is null && tail !is null && length > 0)); 635 } 636 else { 637 assert ((head is null && tail is null) || (head !is null && tail !is null)); 638 } 639 return head is null; 640 } 641 642 void append(T node) nothrow @safe @nogc { 643 assert (getNextOf(node) is null, "Appending non-free node to list"); 644 assert (node !is head && node !is tail, "Appending an invalid node to list"); 645 646 if (empty) { 647 head = tail = node; 648 } 649 else { 650 setNextOf(tail, node); 651 tail = node; 652 } 653 static if (withLength) length++; 654 } 655 void prepend(T node) nothrow @safe @nogc { 656 assert (getNextOf(node) is null && node !is head && node !is tail); 657 658 if (empty) { 659 head = tail = node; 660 } 661 else { 662 setNextOf(node, head); 663 head = node; 664 } 665 static if (withLength) length++; 666 } 667 668 T popHead() nothrow @safe @nogc { 669 assert (!empty); 670 671 auto node = head; 672 head = getNextOf(head); 673 setNextOf(node, null); 674 static if (withLength) length--; 675 676 if (head is null) { 677 tail = null; 678 static if (withLength) assert (length == 0); 679 } 680 681 return node; 682 } 683 684 void removeAll() nothrow { 685 while (!empty) { 686 popHead(); 687 } 688 } 689 690 void splice(_LinkedQueue* second) nothrow { 691 assert (second !is &this); 692 if (second.empty) { 693 return; 694 } 695 if (empty) { 696 head = second.head; 697 } 698 else { 699 setNextOf(tail, second.head); 700 } 701 702 tail = second.tail; 703 static if (withLength) length += second.length; 704 705 // For safety reasons, `second` is emptied (otherwise pop() from it would break this) 706 second.head = null; 707 second.tail = null; 708 static if (withLength) second.length = 0; 709 } 710 711 static struct Range { 712 T front; 713 714 @property empty() const pure @safe @nogc nothrow { 715 return front is null; 716 } 717 void popFront() nothrow { 718 assert (front); 719 front = getNextOf(front); 720 } 721 } 722 @property auto range() nothrow { 723 return Range(head); 724 } 725 } 726 727 alias LinkedQueue(T, string nextAttr="_next") = _LinkedQueue!(T, nextAttr, false); 728 alias LinkedQueueWithLength(T, string nextAttr="_next") = _LinkedQueue!(T, nextAttr, true); 729 730 unittest { 731 import std.stdio; 732 import std..string; 733 734 static struct Node { 735 int value; 736 Node* _next; 737 } 738 Node[10] nodes; 739 foreach(int i, ref n; nodes) { 740 n.value = i; 741 } 742 743 LinkedQueue!(Node*) queue; 744 assert (queue.empty); 745 746 queue.append(&nodes[0]); 747 assert (!queue.empty); 748 749 queue.append(&nodes[1]); 750 queue.append(&nodes[2]); 751 queue.append(&nodes[3]); 752 queue.append(&nodes[4]); 753 queue.append(&nodes[5]); 754 queue.append(&nodes[6]); 755 756 queue.prepend(&nodes[7]); 757 queue.prepend(&nodes[8]); 758 queue.prepend(&nodes[9]); 759 760 void matchElements(R)(R range, int[] expected) { 761 int[] arr; 762 foreach(n; range) { 763 arr ~= n.value; 764 } 765 //writeln(arr); 766 assert (arr == expected, "%s != %s".format(arr, expected)); 767 } 768 769 matchElements(queue.range, [9, 8, 7, 0, 1, 2, 3, 4, 5, 6]); 770 assert (!queue.empty); 771 772 queue.removeAll(); 773 assert (queue.empty); 774 775 LinkedQueue!(Node*) queue2; 776 777 queue.append(&nodes[0]); 778 queue.append(&nodes[1]); 779 queue.append(&nodes[2]); 780 781 queue2.append(&nodes[3]); 782 queue2.append(&nodes[4]); 783 queue2.append(&nodes[5]); 784 785 matchElements(queue.range, [0, 1, 2]); 786 matchElements(queue2.range, [3, 4, 5]); 787 queue.splice(&queue2); 788 assert(queue2.empty); 789 matchElements(queue.range, [0, 1, 2, 3, 4, 5]); 790 } 791 792 unittest { 793 struct Node { 794 static Node[10] theNodes; 795 796 int value; 797 ubyte nextIdx = ubyte.max; 798 799 @property Node* _next() const nothrow @safe @nogc { 800 return nextIdx == ubyte.max ? null : &theNodes[nextIdx]; 801 } 802 @property void _next(Node* n) nothrow @safe @nogc { 803 nextIdx = n is null ? ubyte.max : cast(ubyte)(n - &theNodes[0]); 804 } 805 } 806 807 foreach(int i, ref n; Node.theNodes) { 808 n.value = 100 + i; 809 } 810 811 LinkedQueue!(Node*) queue; 812 assert (queue.empty); 813 814 queue.append(&Node.theNodes[0]); 815 assert (!queue.empty); 816 queue.append(&Node.theNodes[1]); 817 queue.append(&Node.theNodes[2]); 818 queue.append(&Node.theNodes[3]); 819 queue.append(&Node.theNodes[4]); 820 821 int[] arr; 822 foreach(n; queue.range) { 823 arr ~= n.value; 824 } 825 assert(arr == [100, 101, 102, 103, 104]); 826 827 queue.removeAll(); 828 assert (queue.empty); 829 } 830 831 unittest { 832 // Dummy node type for list. This is means primarily to make sure no one in the list implementation is comparing to null instead of using 833 // isValid. Nothing runs in this UT. If it compiles, it's fine 834 struct S { 835 alias OurList = _LinkedList!(S, "_next", "_prev", "owner", false); 836 @property S _next() pure nothrow @nogc @safe { 837 return invalid; 838 } 839 @property void _next(S rhs) pure nothrow @nogc @safe { 840 } 841 @property S _prev() pure nothrow @nogc @safe { 842 return invalid; 843 } 844 @property void _prev(S rhs) pure nothrow @nogc @safe { 845 } 846 OurList* owner; 847 bool isValid() const pure nothrow @safe @nogc { 848 return false; 849 } 850 enum invalid = S.init; 851 } 852 853 S.OurList list; 854 }