1 /** 2 This module implements a singly-linked list container. 3 It can be used as a stack. 4 5 This module is a submodule of $(MREF std, container). 6 7 Source: $(PHOBOSSRC std/container/slist.d) 8 9 Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. 10 11 License: Distributed under the Boost Software License, Version 1.0. 12 (See accompanying file LICENSE_1_0.txt or copy at $(HTTP 13 boost.org/LICENSE_1_0.txt)). 14 15 Authors: $(HTTP erdani.com, Andrei Alexandrescu) 16 17 $(SCRIPT inhibitQuickIndex = 1;) 18 */ 19 module std.container.slist; 20 21 /// 22 @safe unittest 23 { 24 import std.algorithm.comparison : equal; 25 import std.container : SList; 26 27 auto s = SList!int(1, 2, 3); 28 assert(equal(s[], [1, 2, 3])); 29 30 s.removeFront(); 31 assert(equal(s[], [2, 3])); 32 33 s.insertFront([5, 6]); 34 assert(equal(s[], [5, 6, 2, 3])); 35 36 // If you want to apply range operations, simply slice it. 37 import std.algorithm.searching : countUntil; 38 import std.range : popFrontN, walkLength; 39 40 auto sl = SList!int(1, 2, 3, 4, 5); 41 assert(countUntil(sl[], 2) == 1); 42 43 auto r = sl[]; 44 popFrontN(r, 2); 45 assert(walkLength(r) == 3); 46 } 47 48 public import std.container.util; 49 50 /** 51 Implements a simple and fast singly-linked list. 52 It can be used as a stack. 53 54 `SList` uses reference semantics. 55 */ 56 struct SList(T) 57 if (!is(T == shared)) 58 { 59 import std.exception : enforce; 60 import std.range : Take; 61 import std.range.primitives : isInputRange, isForwardRange, ElementType; 62 import std.traits : isImplicitlyConvertible; 63 64 private struct Node 65 { 66 Node * _next; 67 T _payload; 68 } 69 private struct NodeWithoutPayload 70 { 71 Node* _next; 72 } 73 static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof); 74 75 private Node * _root; 76 77 private void initialize() @trusted nothrow pure 78 { 79 if (_root) return; 80 _root = cast (Node*) new NodeWithoutPayload(); 81 } 82 83 private ref inout(Node*) _first() @property @safe nothrow pure inout 84 { 85 assert(_root, "root pointer must not be null"); 86 return _root._next; 87 } 88 89 private static Node * findLastNode(Node * n) 90 { 91 assert(n, "Node n pointer must not be null"); 92 auto ahead = n._next; 93 while (ahead) 94 { 95 n = ahead; 96 ahead = n._next; 97 } 98 return n; 99 } 100 101 private static Node * findLastNode(Node * n, size_t limit) 102 { 103 assert(n, "Node n pointer must not be null"); 104 assert(limit, "limit must be greater than 0"); 105 auto ahead = n._next; 106 while (ahead) 107 { 108 if (!--limit) break; 109 n = ahead; 110 ahead = n._next; 111 } 112 return n; 113 } 114 115 private static Node * findNode(Node * n, Node * findMe) 116 { 117 assert(n, "Node n pointer must not be null"); 118 auto ahead = n._next; 119 while (ahead != findMe) 120 { 121 n = ahead; 122 enforce(n); 123 ahead = n._next; 124 } 125 return n; 126 } 127 128 private static Node* findNodeByValue(Node* n, T value) 129 { 130 if (!n) return null; 131 auto ahead = n._next; 132 while (ahead && ahead._payload != value) 133 { 134 n = ahead; 135 ahead = n._next; 136 } 137 return n; 138 } 139 140 private static auto createNodeChain(Stuff)(Stuff stuff) 141 if (isImplicitlyConvertible!(Stuff, T)) 142 { 143 import std.range : only; 144 return createNodeChain(only(stuff)); 145 } 146 147 private static auto createNodeChain(Stuff)(Stuff stuff) 148 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) 149 { 150 static struct Chain 151 { 152 Node* first; 153 Node* last; 154 size_t length; 155 } 156 157 Chain ch; 158 159 foreach (item; stuff) 160 { 161 auto newNode = new Node(null, item); 162 (ch.first ? ch.last._next : ch.first) = newNode; 163 ch.last = newNode; 164 ++ch.length; 165 } 166 167 return ch; 168 } 169 170 private static size_t insertAfterNode(Stuff)(Node* n, Stuff stuff) 171 { 172 auto ch = createNodeChain(stuff); 173 174 if (!ch.length) return 0; 175 176 ch.last._next = n._next; 177 n._next = ch.first; 178 179 return ch.length; 180 } 181 182 /** 183 Constructor taking a number of nodes 184 */ 185 this(U)(U[] values...) 186 if (isImplicitlyConvertible!(U, T)) 187 { 188 insertFront(values); 189 } 190 191 /** 192 Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) 193 */ 194 this(Stuff)(Stuff stuff) 195 if (isInputRange!Stuff 196 && isImplicitlyConvertible!(ElementType!Stuff, T) 197 && !is(Stuff == T[])) 198 { 199 insertFront(stuff); 200 } 201 202 /** 203 Comparison for equality. 204 205 Complexity: $(BIGOH min(n, n1)) where `n1` is the number of 206 elements in `rhs`. 207 */ 208 bool opEquals(const SList rhs) const 209 { 210 return opEquals(rhs); 211 } 212 213 /// ditto 214 bool opEquals(ref const SList rhs) const 215 { 216 if (_root is rhs._root) return true; 217 if (_root is null) return rhs._root is null || rhs._first is null; 218 if (rhs._root is null) return _root is null || _first is null; 219 220 const(Node) * n1 = _first, n2 = rhs._first; 221 222 for (;; n1 = n1._next, n2 = n2._next) 223 { 224 if (!n1) return !n2; 225 if (!n2 || n1._payload != n2._payload) return false; 226 } 227 } 228 229 /** 230 Defines the container's primary range, which embodies a forward range. 231 */ 232 struct Range 233 { 234 private Node * _head; 235 private this(Node * p) { _head = p; } 236 237 /// Input range primitives. 238 @property bool empty() const { return !_head; } 239 240 /// ditto 241 @property ref T front() 242 { 243 assert(!empty, "SList.Range.front: Range is empty"); 244 return _head._payload; 245 } 246 247 /// ditto 248 void popFront() 249 { 250 assert(!empty, "SList.Range.popFront: Range is empty"); 251 _head = _head._next; 252 } 253 254 /// Forward range primitive. 255 @property Range save() { return this; } 256 257 T moveFront() 258 { 259 import std.algorithm.mutation : move; 260 261 assert(!empty, "SList.Range.moveFront: Range is empty"); 262 return move(_head._payload); 263 } 264 265 bool sameHead(Range rhs) 266 { 267 return _head && _head == rhs._head; 268 } 269 } 270 271 @safe unittest 272 { 273 static assert(isForwardRange!Range); 274 } 275 276 /** 277 Property returning `true` if and only if the container has no 278 elements. 279 280 Complexity: $(BIGOH 1) 281 */ 282 @property bool empty() const 283 { 284 return _root is null || _first is null; 285 } 286 287 /** 288 Duplicates the container. The elements themselves are not transitively 289 duplicated. 290 291 Complexity: $(BIGOH n). 292 */ 293 @property SList dup() 294 { 295 return SList(this[]); 296 } 297 298 /** 299 Returns a range that iterates over all elements of the container, in 300 forward order. 301 302 Complexity: $(BIGOH 1) 303 */ 304 Range opSlice() 305 { 306 if (empty) 307 return Range(null); 308 else 309 return Range(_first); 310 } 311 312 /** 313 Forward to `opSlice().front`. 314 315 Complexity: $(BIGOH 1) 316 */ 317 @property ref T front() 318 { 319 assert(!empty, "SList.front: List is empty"); 320 return _first._payload; 321 } 322 323 @safe unittest 324 { 325 auto s = SList!int(1, 2, 3); 326 s.front = 42; 327 assert(s == SList!int(42, 2, 3)); 328 } 329 330 /** 331 Returns a new `SList` that's the concatenation of `this` and its 332 argument. `opBinaryRight` is only defined if `Stuff` does not 333 define `opBinary`. 334 */ 335 SList opBinary(string op, Stuff)(Stuff rhs) 336 if (op == "~" && is(typeof(SList(rhs)))) 337 { 338 import std.range : chain, only; 339 340 static if (isInputRange!Stuff) 341 alias r = rhs; 342 else 343 auto r = only(rhs); 344 345 return SList(this[].chain(r)); 346 } 347 348 /// ditto 349 SList opBinaryRight(string op, Stuff)(Stuff lhs) 350 if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs)))) 351 { 352 import std.range : chain, only; 353 354 static if (isInputRange!Stuff) 355 alias r = lhs; 356 else 357 auto r = only(lhs); 358 359 return SList(r.chain(this[])); 360 } 361 362 /** 363 Removes all contents from the `SList`. 364 365 Postcondition: `empty` 366 367 Complexity: $(BIGOH 1) 368 */ 369 void clear() 370 { 371 if (_root) 372 _first = null; 373 } 374 375 /** 376 Reverses SList in-place. Performs no memory allocation. 377 378 Complexity: $(BIGOH n) 379 */ 380 void reverse() 381 { 382 if (!empty) 383 { 384 Node* prev; 385 while (_first) 386 { 387 auto next = _first._next; 388 _first._next = prev; 389 prev = _first; 390 _first = next; 391 } 392 _first = prev; 393 } 394 } 395 396 /** 397 Inserts `stuff` to the front of the container. `stuff` can be a 398 value convertible to `T` or a range of objects convertible to $(D 399 T). The stable version behaves the same, but guarantees that ranges 400 iterating over the container are never invalidated. 401 402 Returns: The number of elements inserted 403 404 Complexity: $(BIGOH m), where `m` is the length of `stuff` 405 */ 406 size_t insertFront(Stuff)(Stuff stuff) 407 if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) 408 { 409 initialize(); 410 return insertAfterNode(_root, stuff); 411 } 412 413 /// ditto 414 alias insert = insertFront; 415 416 /// ditto 417 alias stableInsert = insert; 418 419 /// ditto 420 alias stableInsertFront = insertFront; 421 422 /** 423 Picks one value in an unspecified position in the container, removes 424 it from the container, and returns it. The stable version behaves the same, 425 but guarantees that ranges iterating over the container are never invalidated. 426 427 Precondition: `!empty` 428 429 Returns: The element removed. 430 431 Complexity: $(BIGOH 1). 432 */ 433 T removeAny() 434 { 435 import std.algorithm.mutation : move; 436 437 assert(!empty, "SList.removeAny: List is empty"); 438 auto result = move(_first._payload); 439 _first = _first._next; 440 return result; 441 } 442 /// ditto 443 alias stableRemoveAny = removeAny; 444 445 /** 446 Removes the value at the front of the container. The stable version 447 behaves the same, but guarantees that ranges iterating over the 448 container are never invalidated. 449 450 Precondition: `!empty` 451 452 Complexity: $(BIGOH 1). 453 */ 454 void removeFront() 455 { 456 assert(!empty, "SList.removeFront: List is empty"); 457 _first = _first._next; 458 } 459 460 /// ditto 461 alias stableRemoveFront = removeFront; 462 463 /** 464 Removes `howMany` values at the front or back of the 465 container. Unlike the unparameterized versions above, these functions 466 do not throw if they could not remove `howMany` elements. Instead, 467 if $(D howMany > n), all elements are removed. The returned value is 468 the effective number of elements removed. The stable version behaves 469 the same, but guarantees that ranges iterating over the container are 470 never invalidated. 471 472 Returns: The number of elements removed 473 474 Complexity: $(BIGOH howMany * log(n)). 475 */ 476 size_t removeFront(size_t howMany) 477 { 478 size_t result; 479 while (_first && result < howMany) 480 { 481 _first = _first._next; 482 ++result; 483 } 484 return result; 485 } 486 487 /// ditto 488 alias stableRemoveFront = removeFront; 489 490 /** 491 Inserts `stuff` after range `r`, which must be a range 492 previously extracted from this container. Given that all ranges for a 493 list end at the end of the list, this function essentially appends to 494 the list and uses `r` as a potentially fast way to reach the last 495 node in the list. Ideally `r` is positioned near or at the last 496 element of the list. 497 498 `stuff` can be a value convertible to `T` or a range of objects 499 convertible to `T`. The stable version behaves the same, but 500 guarantees that ranges iterating over the container are never 501 invalidated. 502 503 Returns: The number of values inserted. 504 505 Complexity: $(BIGOH k + m), where `k` is the number of elements in 506 `r` and `m` is the length of `stuff`. 507 508 Example: 509 $(RUNNABLE_EXAMPLE 510 -------------------- 511 import std.algorithm, std.container, std.range; 512 513 auto sl = SList!string(["a", "b", "d"]); 514 sl.insertAfter(sl[], "e"); // insert at the end (slowest) 515 assert(equal(sl[], ["a", "b", "d", "e"])); 516 517 sl.insertAfter(take(sl[], 2), "c"); // insert after "b" 518 assert(equal(sl[], ["a", "b", "c", "d", "e"])); 519 -------------------- 520 ) 521 */ 522 523 size_t insertAfter(Stuff)(Range r, Stuff stuff) 524 if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) 525 { 526 initialize(); 527 if (!_first) 528 { 529 enforce(!r._head); 530 return insertFront(stuff); 531 } 532 enforce(r._head); 533 auto n = findLastNode(r._head); 534 return insertAfterNode(n, stuff); 535 } 536 537 /** 538 Similar to `insertAfter` above, but accepts a range bounded in 539 count. This is important for ensuring fast insertions in the middle of 540 the list. For fast insertions after a specified position `r`, use 541 $(D insertAfter(take(r, 1), stuff)). The complexity of that operation 542 only depends on the number of elements in `stuff`. 543 544 Precondition: $(D r.original.empty || r.maxLength > 0) 545 546 Returns: The number of values inserted. 547 548 Complexity: $(BIGOH k + m), where `k` is the number of elements in 549 `r` and `m` is the length of `stuff`. 550 */ 551 size_t insertAfter(Stuff)(Take!Range r, Stuff stuff) 552 if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) 553 { 554 auto orig = r.source; 555 if (!orig._head) 556 { 557 // Inserting after a null range counts as insertion to the 558 // front 559 return insertFront(stuff); 560 } 561 enforce(!r.empty); 562 // Find the last valid element in the range 563 foreach (i; 1 .. r.maxLength) 564 { 565 if (!orig._head._next) break; 566 orig.popFront(); 567 } 568 // insert here 569 return insertAfterNode(orig._head, stuff); 570 } 571 572 /// ditto 573 alias stableInsertAfter = insertAfter; 574 575 /** 576 Removes a range from the list in linear time. 577 578 Returns: An empty range. 579 580 Complexity: $(BIGOH n) 581 */ 582 Range linearRemove(Range r) 583 { 584 if (!_first) 585 { 586 enforce(!r._head); 587 return this[]; 588 } 589 auto n = findNode(_root, r._head); 590 n._next = null; 591 return Range(null); 592 } 593 594 /** 595 Removes a `Take!Range` from the list in linear time. 596 597 Returns: A range comprehending the elements after the removed range. 598 599 Complexity: $(BIGOH n) 600 */ 601 Range linearRemove(Take!Range r) 602 { 603 auto orig = r.source; 604 // We have something to remove here 605 if (orig._head == _first) 606 { 607 // remove straight from the head of the list 608 for (; !r.empty; r.popFront()) 609 { 610 removeFront(); 611 } 612 return this[]; 613 } 614 if (!r.maxLength) 615 { 616 // Nothing to remove, return the range itself 617 return orig; 618 } 619 // Remove from somewhere in the middle of the list 620 enforce(_first); 621 auto n1 = findNode(_root, orig._head); 622 auto n2 = findLastNode(orig._head, r.maxLength); 623 n1._next = n2._next; 624 return Range(n1._next); 625 } 626 627 /// ditto 628 alias stableLinearRemove = linearRemove; 629 630 /** 631 Removes the first occurence of an element from the list in linear time. 632 633 Returns: True if the element existed and was successfully removed, false otherwise. 634 635 Params: 636 value = value of the node to be removed 637 638 Complexity: $(BIGOH n) 639 */ 640 bool linearRemoveElement(T value) 641 { 642 auto n1 = findNodeByValue(_root, value); 643 644 if (n1 && n1._next) 645 { 646 n1._next = n1._next._next; 647 return true; 648 } 649 650 return false; 651 } 652 } 653 654 @safe unittest 655 { 656 import std.algorithm.comparison : equal; 657 658 auto e = SList!int(); 659 auto b = e.linearRemoveElement(2); 660 assert(b == false); 661 assert(e.empty()); 662 auto a = SList!int(-1, 1, 2, 1, 3, 4); 663 b = a.linearRemoveElement(1); 664 assert(equal(a[], [-1, 2, 1, 3, 4])); 665 assert(b == true); 666 b = a.linearRemoveElement(-1); 667 assert(b == true); 668 assert(equal(a[], [2, 1, 3, 4])); 669 b = a.linearRemoveElement(1); 670 assert(b == true); 671 assert(equal(a[], [2, 3, 4])); 672 b = a.linearRemoveElement(2); 673 assert(b == true); 674 b = a.linearRemoveElement(20); 675 assert(b == false); 676 assert(equal(a[], [3, 4])); 677 b = a.linearRemoveElement(4); 678 assert(b == true); 679 assert(equal(a[], [3])); 680 b = a.linearRemoveElement(3); 681 assert(b == true); 682 assert(a.empty()); 683 a.linearRemoveElement(3); 684 } 685 686 @safe unittest 687 { 688 import std.algorithm.comparison : equal; 689 690 auto a = SList!int(5); 691 auto b = a; 692 auto r = a[]; 693 a.insertFront(1); 694 b.insertFront(2); 695 assert(equal(a[], [2, 1, 5])); 696 assert(equal(b[], [2, 1, 5])); 697 r.front = 9; 698 assert(equal(a[], [2, 1, 9])); 699 assert(equal(b[], [2, 1, 9])); 700 } 701 702 @safe unittest 703 { 704 auto s = SList!int(1, 2, 3); 705 auto n = s.findLastNode(s._root); 706 assert(n && n._payload == 3); 707 } 708 709 @safe unittest 710 { 711 import std.range.primitives; 712 auto s = SList!int(1, 2, 5, 10); 713 assert(walkLength(s[]) == 4); 714 } 715 716 @safe unittest 717 { 718 import std.range : take; 719 auto src = take([0, 1, 2, 3], 3); 720 auto s = SList!int(src); 721 assert(s == SList!int(0, 1, 2)); 722 } 723 724 @safe unittest 725 { 726 auto a = SList!int(); 727 auto b = SList!int(); 728 auto c = a ~ b[]; 729 assert(c.empty); 730 } 731 732 @safe unittest 733 { 734 auto a = SList!int(1, 2, 3); 735 auto b = SList!int(4, 5, 6); 736 auto c = a ~ b[]; 737 assert(c == SList!int(1, 2, 3, 4, 5, 6)); 738 } 739 740 @safe unittest 741 { 742 auto a = SList!int(1, 2, 3); 743 auto b = [4, 5, 6]; 744 auto c = a ~ b; 745 assert(c == SList!int(1, 2, 3, 4, 5, 6)); 746 } 747 748 @safe unittest 749 { 750 auto a = SList!int(1, 2, 3); 751 auto c = a ~ 4; 752 assert(c == SList!int(1, 2, 3, 4)); 753 } 754 755 @safe unittest 756 { 757 auto a = SList!int(2, 3, 4); 758 auto b = 1 ~ a; 759 assert(b == SList!int(1, 2, 3, 4)); 760 } 761 762 @safe unittest 763 { 764 auto a = [1, 2, 3]; 765 auto b = SList!int(4, 5, 6); 766 auto c = a ~ b; 767 assert(c == SList!int(1, 2, 3, 4, 5, 6)); 768 } 769 770 @safe unittest 771 { 772 auto s = SList!int(1, 2, 3, 4); 773 s.insertFront([ 42, 43 ]); 774 assert(s == SList!int(42, 43, 1, 2, 3, 4)); 775 } 776 777 @safe unittest 778 { 779 auto s = SList!int(1, 2, 3); 780 assert(s.removeAny() == 1); 781 assert(s == SList!int(2, 3)); 782 assert(s.stableRemoveAny() == 2); 783 assert(s == SList!int(3)); 784 } 785 786 @safe unittest 787 { 788 import std.algorithm.comparison : equal; 789 790 auto s = SList!int(1, 2, 3); 791 s.removeFront(); 792 assert(equal(s[], [2, 3])); 793 s.stableRemoveFront(); 794 assert(equal(s[], [3])); 795 } 796 797 @safe unittest 798 { 799 auto s = SList!int(1, 2, 3, 4, 5, 6, 7); 800 assert(s.removeFront(3) == 3); 801 assert(s == SList!int(4, 5, 6, 7)); 802 } 803 804 @safe unittest 805 { 806 auto a = SList!int(1, 2, 3); 807 auto b = SList!int(1, 2, 3); 808 assert(a.insertAfter(a[], b[]) == 3); 809 } 810 811 @safe unittest 812 { 813 import std.range : take; 814 auto s = SList!int(1, 2, 3, 4); 815 auto r = take(s[], 2); 816 assert(s.insertAfter(r, 5) == 1); 817 assert(s == SList!int(1, 2, 5, 3, 4)); 818 } 819 820 @safe unittest 821 { 822 import std.algorithm.comparison : equal; 823 import std.range : take; 824 825 // insertAfter documentation example 826 auto sl = SList!string(["a", "b", "d"]); 827 sl.insertAfter(sl[], "e"); // insert at the end (slowest) 828 assert(equal(sl[], ["a", "b", "d", "e"])); 829 sl.insertAfter(take(sl[], 2), "c"); // insert after "b" 830 assert(equal(sl[], ["a", "b", "c", "d", "e"])); 831 } 832 833 @safe unittest 834 { 835 import std.range.primitives; 836 auto s = SList!int(1, 2, 3, 4, 5); 837 auto r = s[]; 838 popFrontN(r, 3); 839 auto r1 = s.linearRemove(r); 840 assert(s == SList!int(1, 2, 3)); 841 assert(r1.empty); 842 } 843 844 @safe unittest 845 { 846 auto s = SList!int(1, 2, 3, 4, 5); 847 auto r = s[]; 848 auto r1 = s.linearRemove(r); 849 assert(s == SList!int()); 850 assert(r1.empty); 851 } 852 853 @safe unittest 854 { 855 import std.algorithm.comparison : equal; 856 import std.range; 857 858 auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 859 auto r = s[]; 860 popFrontN(r, 3); 861 auto r1 = take(r, 4); 862 assert(equal(r1, [4, 5, 6, 7])); 863 auto r2 = s.linearRemove(r1); 864 assert(s == SList!int(1, 2, 3, 8, 9, 10)); 865 assert(equal(r2, [8, 9, 10])); 866 } 867 868 @safe unittest 869 { 870 import std.range.primitives; 871 auto lst = SList!int(1, 5, 42, 9); 872 assert(!lst.empty); 873 assert(lst.front == 1); 874 assert(walkLength(lst[]) == 4); 875 876 auto lst2 = lst ~ [ 1, 2, 3 ]; 877 assert(walkLength(lst2[]) == 7); 878 879 auto lst3 = lst ~ [ 7 ]; 880 assert(walkLength(lst3[]) == 5); 881 } 882 883 @safe unittest 884 { 885 auto s = make!(SList!int)(1, 2, 3); 886 } 887 888 // https://issues.dlang.org/show_bug.cgi?id=5193 889 @safe unittest 890 { 891 static struct Data 892 { 893 const int val; 894 } 895 SList!Data list; 896 } 897 898 @safe unittest 899 { 900 auto s = SList!int([1, 2, 3]); 901 s.front = 5; //test frontAssign 902 assert(s.front == 5); 903 auto r = s[]; 904 r.front = 1; //test frontAssign 905 assert(r.front == 1); 906 } 907 908 // https://issues.dlang.org/show_bug.cgi?id=14920 909 @safe unittest 910 { 911 SList!int s; 912 s.insertAfter(s[], 1); 913 assert(s.front == 1); 914 } 915 916 // https://issues.dlang.org/show_bug.cgi?id=15659 917 @safe unittest 918 { 919 SList!int s; 920 s.clear(); 921 } 922 923 @safe unittest 924 { 925 SList!int s; 926 s.reverse(); 927 } 928 929 @safe unittest 930 { 931 import std.algorithm.comparison : equal; 932 933 auto s = SList!int([1, 2, 3]); 934 assert(s[].equal([1, 2, 3])); 935 936 s.reverse(); 937 assert(s[].equal([3, 2, 1])); 938 } 939 940 @safe unittest 941 { 942 auto s = SList!int([4, 6, 8, 12, 16]); 943 auto d = s.dup; 944 assert(d !is s); 945 assert(d == s); 946 }