2 This module implements a generic doubly-linked list container.
3 It can be used as a queue, dequeue or stack.
5 This module is a submodule of $(MREF std, container).
7 Source: $(PHOBOSSRC std/container/dlist.d)
9 Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
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)).
15 Authors: $(HTTP erdani.com, Andrei Alexandrescu)
17 $(SCRIPT inhibitQuickIndex = 1;)
19 module std.container.dlist;
24 import std.algorithm.comparison : equal;
25 import std.container : DList;
27 auto s = DList!int(1, 2, 3);
28 assert(equal(s[], [1, 2, 3]));
31 assert(equal(s[], [2, 3]));
33 assert(equal(s[], [2]));
35 s.insertFront([4, 5]);
36 assert(equal(s[], [4, 5, 2]));
38 assert(equal(s[], [4, 5, 2, 6, 7]));
40 // If you want to apply range operations, simply slice it.
41 import std.algorithm.searching : countUntil;
42 import std.range : popFrontN, popBackN, walkLength;
44 auto sl = DList!int([1, 2, 3, 4, 5]);
45 assert(countUntil(sl[], 2) == 1);
51 assert(walkLength(r) == 1);
53 // DList.Range can be used to remove elements from the list it spans
54 auto nl = DList!int([1, 2, 3, 4, 5]);
55 for (auto rn = nl[]; !rn.empty;)
56 if (rn.front % 2 == 0)
60 assert(equal(nl[], [1, 3, 5]));
64 assert(equal(nl[], [1]));
67 import std.range.primitives;
70 public import std.container.util;
73 A DList Node without payload. Used to handle the sentinel node (henceforth "sentinode").
75 Also used for parts of the code that don't depend on the payload type.
77 private struct BaseNode
79 private BaseNode* _prev = null;
80 private BaseNode* _next = null;
83 Gets the payload associated with this node.
84 This is trusted because all nodes are associated with a payload, even
86 It is also not possible to mix Nodes in DLists of different types.
87 This function is implemented as a member function here, as UFCS does not
90 ref inout(T) getPayload(T)() inout @trusted
92 return (cast(inout(DList!T.PayNode)*)&this)._payload;
95 // Helper: Given nodes p and n, connects them.
96 static void connect(BaseNode* p, BaseNode* n) @safe nothrow pure
104 The base DList Range. Contains Range primitives that don't depend on payload type.
106 private struct DRange
110 static assert(isBidirectionalRange!DRange);
111 static assert(is(ElementType!DRange == BaseNode*));
115 private BaseNode* _first;
116 private BaseNode* _last;
118 private this(BaseNode* first, BaseNode* last)
120 assert((first is null) == (last is null), "Dlist.Range.this: Invalid arguments");
124 private this(BaseNode* n)
130 bool empty() const scope
132 assert((_first is null) == (_last is null), "DList.Range: Invalidated state");
136 @property BaseNode* front() return scope
138 assert(!empty, "DList.Range.front: Range is empty");
142 void popFront() scope
144 assert(!empty, "DList.Range.popFront: Range is empty");
147 _first = _last = null;
151 assert(_first._next && _first is _first._next._prev, "DList.Range: Invalidated state");
152 _first = _first._next;
156 @property BaseNode* back() return scope
158 assert(!empty, "DList.Range.front: Range is empty");
164 assert(!empty, "DList.Range.popBack: Range is empty");
167 _first = _last = null;
171 assert(_last._prev && _last is _last._prev._next, "DList.Range: Invalidated state");
176 /// Forward range primitive.
177 @property DRange save() return scope { return this; }
181 Implements a doubly-linked list.
183 `DList` uses reference semantics.
187 import std.range : Take;
190 A Node with a Payload. A PayNode.
199 inout(BaseNode)* asBaseNode() inout @trusted
206 private BaseNode* _root;
210 //Construct as new PayNode, and returns it as a BaseNode.
211 static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null)
213 return (new PayNode(BaseNode(prev, next), arg)).asBaseNode();
216 void initialize() nothrow @safe pure
219 //Note: We allocate a PayNode for safety reasons.
220 _root = (new PayNode()).asBaseNode();
221 _root._next = _root._prev = _root;
223 ref inout(BaseNode*) _first() @property @safe nothrow pure inout
225 assert(_root, "Root pointer must not be null");
228 ref inout(BaseNode*) _last() @property @safe nothrow pure inout
230 assert(_root, "Root pointer must not be null");
236 Constructor taking a number of nodes
238 this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
244 Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
246 this(Stuff)(Stuff stuff)
247 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
253 Comparison for equality.
255 Complexity: $(BIGOH min(n, n1)) where `n1` is the number of
258 bool opEquals()(ref const DList rhs) const
259 if (is(typeof(front == front)))
262 const lroot = lhs._root;
263 const rroot = rhs._root;
265 if (lroot is rroot) return true;
266 if (lroot is null) return rroot is rroot._next;
267 if (rroot is null) return lroot is lroot._next;
269 const(BaseNode)* pl = lhs._first;
270 const(BaseNode)* pr = rhs._first;
273 if (pl is lroot) return pr is rroot;
274 if (pr is rroot) return false;
276 // !== because of NaN
277 if (!(pl.getPayload!T() == pr.getPayload!T())) return false;
285 Defines the container's primary range, which embodies a bidirectional range.
289 static assert(isBidirectionalRange!Range);
294 private this(BaseNode* first, BaseNode* last)
296 _base = DRange(first, last);
298 private this(BaseNode* n)
303 @property ref T front()
305 return _base.front.getPayload!T();
308 @property ref T back()
310 return _base.back.getPayload!T();
313 //Note: shadows base DRange.save.
314 //Necessary for static covariance.
315 @property Range save() { return this; }
319 Property returning `true` if and only if the container has no
322 Complexity: $(BIGOH 1)
324 bool empty() @property const nothrow
326 return _root is null || _root is _first;
330 Removes all contents from the `DList`.
332 Postcondition: `empty`
334 Complexity: $(BIGOH 1)
338 //remove actual elements.
343 Duplicates the container. The elements themselves are not transitively
346 Complexity: $(BIGOH n).
348 @property DList dup()
350 return DList(this[]);
354 Returns a range that iterates over all elements of the container, in
357 Complexity: $(BIGOH 1)
362 return Range(null, null);
364 return Range(_first, _last);
368 Forward to `opSlice().front`.
370 Complexity: $(BIGOH 1)
372 @property ref inout(T) front() inout
374 assert(!empty, "DList.front: List is empty");
375 return _first.getPayload!T();
379 Forward to `opSlice().back`.
381 Complexity: $(BIGOH 1)
383 @property ref inout(T) back() inout
385 assert(!empty, "DList.back: List is empty");
386 return _last.getPayload!T();
389 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
390 /+ BEGIN CONCAT FUNCTIONS HERE +/
391 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
394 Returns a new `DList` that's the concatenation of `this` and its
397 DList opBinary(string op, Stuff)(Stuff rhs)
398 if (op == "~" && is(typeof(insertBack(rhs))))
406 Returns a new `DList` that's the concatenation of the argument `lhs`
409 DList opBinaryRight(string op, Stuff)(Stuff lhs)
410 if (op == "~" && is(typeof(insertFront(lhs))))
413 ret.insertFront(lhs);
418 Appends the contents of the argument `rhs` into `this`.
420 DList opOpAssign(string op, Stuff)(Stuff rhs)
421 if (op == "~" && is(typeof(insertBack(rhs))))
427 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
428 /+ BEGIN INSERT FUNCTIONS HERE +/
429 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
432 Inserts `stuff` to the front/back of the container. `stuff` can be a
433 value convertible to `T` or a range of objects convertible to $(D
434 T). The stable version behaves the same, but guarantees that ranges
435 iterating over the container are never invalidated.
437 Returns: The number of elements inserted
439 Complexity: $(BIGOH log(n))
441 size_t insertFront(Stuff)(Stuff stuff)
444 return insertAfterNode(_root, stuff);
448 size_t insertBack(Stuff)(Stuff stuff)
451 return insertBeforeNode(_root, stuff);
455 alias insert = insertBack;
458 alias stableInsert = insert;
461 alias stableInsertFront = insertFront;
464 alias stableInsertBack = insertBack;
467 Inserts `stuff` after range `r`, which must be a non-empty range
468 previously extracted from this container.
470 `stuff` can be a value convertible to `T` or a range of objects
471 convertible to `T`. The stable version behaves the same, but
472 guarantees that ranges iterating over the container are never
475 Returns: The number of values inserted.
477 Complexity: $(BIGOH k + m), where `k` is the number of elements in
478 `r` and `m` is the length of `stuff`.
480 size_t insertBefore(Stuff)(Range r, Stuff stuff)
483 return insertBeforeNode(r._first, stuff);
487 return insertAfterNode(_root, stuff);
492 alias stableInsertBefore = insertBefore;
495 size_t insertAfter(Stuff)(Range r, Stuff stuff)
498 return insertAfterNode(r._last, stuff);
502 return insertBeforeNode(_root, stuff);
507 alias stableInsertAfter = insertAfter;
509 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
510 /+ BEGIN REMOVE FUNCTIONS HERE +/
511 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
514 Picks one value in an unspecified position in the container, removes
515 it from the container, and returns it. The stable version behaves the same,
516 but guarantees that ranges iterating over the container are never invalidated.
518 Precondition: `!empty`
520 Returns: The element removed.
522 Complexity: $(BIGOH 1).
526 import std.algorithm.mutation : move;
528 assert(!empty, "DList.removeAny: List is empty");
529 auto result = move(back);
534 alias stableRemoveAny = removeAny;
537 Removes the value at the front/back of the container. The stable version
538 behaves the same, but guarantees that ranges iterating over the
539 container are never invalidated.
541 Precondition: `!empty`
543 Complexity: $(BIGOH 1).
547 assert(!empty, "DList.removeFront: List is empty");
548 assert(_root is _first._prev, "DList: Inconsistent state");
549 BaseNode.connect(_root, _first._next);
553 alias stableRemoveFront = removeFront;
558 assert(!empty, "DList.removeBack: List is empty");
559 assert(_last._next is _root, "DList: Inconsistent state");
560 BaseNode.connect(_last._prev, _root);
564 alias stableRemoveBack = removeBack;
567 Removes `howMany` values at the front or back of the
568 container. Unlike the unparameterized versions above, these functions
569 do not throw if they could not remove `howMany` elements. Instead,
570 if $(D howMany > n), all elements are removed. The returned value is
571 the effective number of elements removed. The stable version behaves
572 the same, but guarantees that ranges iterating over the container are
575 Returns: The number of elements removed
577 Complexity: $(BIGOH howMany).
579 size_t removeFront(size_t howMany)
581 if (!_root) return 0;
584 while (p !is _root && result < howMany)
589 BaseNode.connect(_root, p);
594 alias stableRemoveFront = removeFront;
597 size_t removeBack(size_t howMany)
599 if (!_root) return 0;
602 while (p !is _root && result < howMany)
607 BaseNode.connect(p, _root);
612 alias stableRemoveBack = removeBack;
615 Removes all elements belonging to `r`, which must be a range
616 obtained originally from this container.
618 Returns: A range spanning the remaining elements in the container that
619 initially were right after `r`.
621 Complexity: $(BIGOH 1)
623 Range remove(Range r)
628 assert(_root !is null, "Cannot remove from an un-initialized List");
629 assert(r._first, "Remove: Range is empty");
631 BaseNode.connect(r._first._prev, r._last._next);
632 auto after = r._last._next;
634 return Range(null, null);
636 return Range(after, _last);
640 Range linearRemove(Range r)
646 alias stableRemove = remove;
649 Removes first element of `r`, wich must be a range obtained originally
650 from this container, from both DList instance and range `r`.
652 Compexity: $(BIGOH 1)
654 void popFirstOf(ref Range r)
656 assert(_root !is null, "Cannot remove from an un-initialized List");
657 assert(r._first, "popFirstOf: Range is empty");
658 auto prev = r._first._prev;
659 auto next = r._first._next;
661 BaseNode.connect(prev, next);
665 Removes last element of `r`, wich must be a range obtained originally
666 from this container, from both DList instance and range `r`.
668 Compexity: $(BIGOH 1)
670 void popLastOf(ref Range r)
672 assert(_root !is null, "Cannot remove from an un-initialized List");
673 assert(r._first, "popLastOf: Range is empty");
674 auto prev = r._last._prev;
675 auto next = r._last._next;
677 BaseNode.connect(prev, next);
681 `linearRemove` functions as `remove`, but also accepts ranges that are
682 result the of a `take` operation. This is a convenient way to remove a
683 fixed amount of elements from the range.
685 Complexity: $(BIGOH r.walkLength)
687 Range linearRemove(Take!Range r)
689 assert(_root !is null, "Cannot remove from an un-initialized List");
690 assert(r.source._first, "Remove: Range is empty");
692 BaseNode* first = r.source._first;
693 BaseNode* last = null;
696 last = r.source._first;
698 } while ( !r.empty );
700 return remove(Range(first, last));
704 alias stableLinearRemove = linearRemove;
707 Removes the first occurence of an element from the list in linear time.
709 Returns: True if the element existed and was successfully removed, false otherwise.
712 value = value of the node to be removed
714 Complexity: $(BIGOH n)
716 bool linearRemoveElement(T value)
718 auto n1 = findNodeByValue(_root, value);
721 auto n2 = n1._next._next;
722 BaseNode.connect(n1, n2);
732 BaseNode* findNodeByValue(BaseNode* n, T value)
735 auto ahead = n._next;
736 while (ahead && ahead.getPayload!T() != value)
740 if (ahead == _last._next) return null;
745 // Helper: Inserts stuff before the node n.
746 size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
747 if (isImplicitlyConvertible!(Stuff, T))
749 auto p = createNode(stuff, n._prev, n);
755 size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
756 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
758 if (stuff.empty) return 0;
760 Range r = createRange(stuff, result);
761 BaseNode.connect(n._prev, r._first);
762 BaseNode.connect(r._last, n);
766 // Helper: Inserts stuff after the node n.
767 size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
768 if (isImplicitlyConvertible!(Stuff, T))
770 auto p = createNode(stuff, n, n._next);
776 size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
777 if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
779 if (stuff.empty) return 0;
781 Range r = createRange(stuff, result);
782 BaseNode.connect(r._last, n._next);
783 BaseNode.connect(n, r._first);
787 // Helper: Creates a chain of nodes from the range stuff.
788 Range createRange(Stuff)(ref Stuff stuff, ref size_t result)
790 BaseNode* first = createNode(stuff.front);
791 BaseNode* last = first;
793 for ( stuff.popFront() ; !stuff.empty ; stuff.popFront() )
795 auto p = createNode(stuff.front, last);
796 last = last._next = p;
799 return Range(first, last);
805 import std.algorithm.comparison : equal;
807 auto e = DList!int();
808 auto b = e.linearRemoveElement(1);
811 auto a = DList!int(-1, 1, 2, 1, 3, 4);
812 b = a.linearRemoveElement(1);
813 assert(equal(a[], [-1, 2, 1, 3, 4]));
815 b = a.linearRemoveElement(-1);
817 assert(equal(a[], [2, 1, 3, 4]));
818 b = a.linearRemoveElement(1);
820 assert(equal(a[], [2, 3, 4]));
821 b = a.linearRemoveElement(2);
823 b = a.linearRemoveElement(20);
825 assert(equal(a[], [3, 4]));
826 b = a.linearRemoveElement(4);
828 assert(equal(a[], [3]));
829 b = a.linearRemoveElement(3);
832 a.linearRemoveElement(3);
837 import std.algorithm.comparison : equal;
839 //Tests construction signatures
840 alias IntList = DList!int;
842 auto a1 = IntList(0);
843 auto a2 = IntList(0, 1);
844 auto a3 = IntList([0]);
845 auto a4 = IntList([0, 1]);
848 assert(equal(a1[], [0]));
849 assert(equal(a2[], [0, 1]));
850 assert(equal(a3[], [0]));
851 assert(equal(a4[], [0, 1]));
856 import std.algorithm.comparison : equal;
858 alias IntList = DList!int;
859 IntList list = IntList([0,1,2,3]);
860 assert(equal(list[],[0,1,2,3]));
861 list.insertBack([4,5,6,7]);
862 assert(equal(list[],[0,1,2,3,4,5,6,7]));
865 list.insertFront([0,1,2,3]);
866 assert(equal(list[],[0,1,2,3]));
867 list.insertFront([4,5,6,7]);
868 assert(equal(list[],[4,5,6,7,0,1,2,3]));
873 import std.algorithm.comparison : equal;
874 import std.range : take;
876 alias IntList = DList!int;
877 IntList list = IntList([0,1,2,3]);
879 for ( ; !range.empty; range.popFront())
881 int item = range.front;
884 list.stableLinearRemove(take(range, 1));
888 assert(equal(list[],[0,1,3]));
890 list = IntList([0,1,2,3]);
892 for ( ; !range.empty; range.popFront())
894 int item = range.front;
897 list.stableLinearRemove(take(range,2));
901 assert(equal(list[],[0,1]));
903 list = IntList([0,1,2,3]);
905 for ( ; !range.empty; range.popFront())
907 int item = range.front;
910 list.stableLinearRemove(take(range,2));
914 assert(equal(list[],[2,3]));
916 list = IntList([0,1,2,3]);
918 for ( ; !range.empty; range.popFront())
920 int item = range.front;
923 list.stableLinearRemove(take(range,2));
927 assert(equal(list[],[0,3]));
932 import std.algorithm.comparison : equal;
934 auto dl = DList!int([1, 2, 3, 4, 5]);
938 assert(equal(dl[], [1, 3, 4, 5]));
939 assert(equal(r, [3, 4, 5]));
942 assert(equal(dl[], [1, 3, 5]));
943 assert(equal(r, [3]));
956 import std.algorithm.comparison : equal;
958 auto dl = DList!string(["a", "b", "d"]);
959 dl.insertAfter(dl[], "e"); // insert at the end
960 assert(equal(dl[], ["a", "b", "d", "e"]));
962 dlr.popBack(); dlr.popBack();
963 dl.insertAfter(dlr, "c"); // insert after "b"
964 assert(equal(dl[], ["a", "b", "c", "d", "e"]));
969 import std.algorithm.comparison : equal;
971 auto dl = DList!string(["a", "b", "d"]);
972 dl.insertBefore(dl[], "e"); // insert at the front
973 assert(equal(dl[], ["e", "a", "b", "d"]));
975 dlr.popFront(); dlr.popFront();
976 dl.insertBefore(dlr, "c"); // insert before "b"
977 assert(equal(dl[], ["e", "a", "c", "b", "d"]));
982 auto d = DList!int([1, 2, 3]);
983 d.front = 5; //test frontAssign
984 assert(d.front == 5);
990 // https://issues.dlang.org/show_bug.cgi?id=8895
993 auto a = make!(DList!int)(1,2,3,4);
994 auto b = make!(DList!int)(1,2,3,4);
995 auto c = make!(DList!int)(1,2,3,5);
996 auto d = make!(DList!int)(1,2,3,4,5);
997 assert(a == b); // this better terminate!
1004 auto d = DList!int([1, 2, 3]);
1005 d.front = 5; //test frontAssign
1006 assert(d.front == 5);
1009 assert(r.back == 1);
1014 auto a = DList!int();
1015 assert(a.removeFront(10) == 0);
1016 a.insert([1, 2, 3]);
1017 assert(a.removeFront(10) == 3);
1023 import std.algorithm.comparison : equal;
1025 //Verify all flavors of ~
1026 auto a = DList!int();
1027 auto b = DList!int();
1028 auto c = DList!int([1, 2, 3]);
1029 auto d = DList!int([4, 5, 6]);
1031 assert((a ~ b[])[].empty);
1032 assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6]));
1033 assert(c[].equal([1, 2, 3]));
1034 assert(d[].equal([4, 5, 6]));
1036 assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6]));
1037 assert(c[].equal([1, 2, 3]));
1038 assert(d[].equal([4, 5, 6]));
1041 assert(a[].equal([1, 2, 3]));
1042 assert(c[].equal([1, 2, 3]));
1045 assert(a[].equal([1, 2, 3, 4, 5, 6]));
1046 assert(d[].equal([4, 5, 6]));
1049 assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
1059 import std.algorithm.comparison : equal;
1061 // https://issues.dlang.org/show_bug.cgi?id=8905
1062 auto a = DList!int([1, 2, 3, 4]);
1064 a.stableRemoveBack();
1065 a.stableInsertBack(7);
1066 assert(a[].equal([1, 2, 3, 7]));
1069 // https://issues.dlang.org/show_bug.cgi?id=12566
1072 auto dl2 = DList!int([2,7]);
1074 assert(dl2[].walkLength == 1);
1076 assert(dl2.empty, "not empty?!");
1079 // https://issues.dlang.org/show_bug.cgi?id=13076
1087 // https://issues.dlang.org/show_bug.cgi?id=13425
1090 import std.range : drop, take;
1091 auto list = DList!int([1,2,3,4,5]);
1092 auto r = list[].drop(4); // r is a view of the last element of list
1093 assert(r.front == 5 && r.walkLength == 1);
1094 r = list.linearRemove(r.take(1));
1095 assert(r.empty); // fails
1098 // https://issues.dlang.org/show_bug.cgi?id=14300
1102 static class Test : ITest {}
1104 DList!ITest().insertBack(new Test());
1107 // https://issues.dlang.org/show_bug.cgi?id=15263
1110 import std.range : iota;
1111 auto a = DList!int();
1112 a.insertFront(iota(0, 5)); // can insert range with non-ref front
1113 assert(a.front == 0 && a.back == 4);