| /** | |
| This module implements a singly-linked list container. | |
| It can be used as a stack. | |
| This module is a submodule of $(MREF std, container). | |
| Source: $(PHOBOSSRC std/container/slist.d) | |
| Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders. | |
| License: Distributed under the Boost Software License, Version 1.0. | |
| (See accompanying file LICENSE_1_0.txt or copy at $(HTTP | |
| boost.org/LICENSE_1_0.txt)). | |
| Authors: $(HTTP erdani.com, Andrei Alexandrescu) | |
| $(SCRIPT inhibitQuickIndex = 1;) | |
| */ | |
| module std.container.slist; | |
| /// | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.container : SList; | |
| auto s = SList!int(1, 2, 3); | |
| assert(equal(s[], [1, 2, 3])); | |
| s.removeFront(); | |
| assert(equal(s[], [2, 3])); | |
| s.insertFront([5, 6]); | |
| assert(equal(s[], [5, 6, 2, 3])); | |
| // If you want to apply range operations, simply slice it. | |
| import std.algorithm.searching : countUntil; | |
| import std.range : popFrontN, walkLength; | |
| auto sl = SList!int(1, 2, 3, 4, 5); | |
| assert(countUntil(sl[], 2) == 1); | |
| auto r = sl[]; | |
| popFrontN(r, 2); | |
| assert(walkLength(r) == 3); | |
| } | |
| public import std.container.util; | |
| /** | |
| Implements a simple and fast singly-linked list. | |
| It can be used as a stack. | |
| `SList` uses reference semantics. | |
| */ | |
| struct SList(T) | |
| if (!is(T == shared)) | |
| { | |
| import std.exception : enforce; | |
| import std.range : Take; | |
| import std.range.primitives : isInputRange, isForwardRange, ElementType; | |
| import std.traits : isImplicitlyConvertible; | |
| private struct Node | |
| { | |
| Node * _next; | |
| T _payload; | |
| } | |
| private struct NodeWithoutPayload | |
| { | |
| Node* _next; | |
| } | |
| static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof); | |
| private Node * _root; | |
| private void initialize() @trusted nothrow pure | |
| { | |
| if (_root) return; | |
| _root = cast (Node*) new NodeWithoutPayload(); | |
| } | |
| private ref inout(Node*) _first() @property @safe nothrow pure inout | |
| { | |
| assert(_root, "root pointer must not be null"); | |
| return _root._next; | |
| } | |
| private static Node * findLastNode(Node * n) | |
| { | |
| assert(n, "Node n pointer must not be null"); | |
| auto ahead = n._next; | |
| while (ahead) | |
| { | |
| n = ahead; | |
| ahead = n._next; | |
| } | |
| return n; | |
| } | |
| private static Node * findLastNode(Node * n, size_t limit) | |
| { | |
| assert(n, "Node n pointer must not be null"); | |
| assert(limit, "limit must be greater than 0"); | |
| auto ahead = n._next; | |
| while (ahead) | |
| { | |
| if (!--limit) break; | |
| n = ahead; | |
| ahead = n._next; | |
| } | |
| return n; | |
| } | |
| private static Node * findNode(Node * n, Node * findMe) | |
| { | |
| assert(n, "Node n pointer must not be null"); | |
| auto ahead = n._next; | |
| while (ahead != findMe) | |
| { | |
| n = ahead; | |
| enforce(n); | |
| ahead = n._next; | |
| } | |
| return n; | |
| } | |
| private static Node* findNodeByValue(Node* n, T value) | |
| { | |
| if (!n) return null; | |
| auto ahead = n._next; | |
| while (ahead && ahead._payload != value) | |
| { | |
| n = ahead; | |
| ahead = n._next; | |
| } | |
| return n; | |
| } | |
| private static auto createNodeChain(Stuff)(Stuff stuff) | |
| if (isImplicitlyConvertible!(Stuff, T)) | |
| { | |
| import std.range : only; | |
| return createNodeChain(only(stuff)); | |
| } | |
| private static auto createNodeChain(Stuff)(Stuff stuff) | |
| if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) | |
| { | |
| static struct Chain | |
| { | |
| Node* first; | |
| Node* last; | |
| size_t length; | |
| } | |
| Chain ch; | |
| foreach (item; stuff) | |
| { | |
| auto newNode = new Node(null, item); | |
| (ch.first ? ch.last._next : ch.first) = newNode; | |
| ch.last = newNode; | |
| ++ch.length; | |
| } | |
| return ch; | |
| } | |
| private static size_t insertAfterNode(Stuff)(Node* n, Stuff stuff) | |
| { | |
| auto ch = createNodeChain(stuff); | |
| if (!ch.length) return 0; | |
| ch.last._next = n._next; | |
| n._next = ch.first; | |
| return ch.length; | |
| } | |
| /** | |
| Constructor taking a number of nodes | |
| */ | |
| this(U)(U[] values...) | |
| if (isImplicitlyConvertible!(U, T)) | |
| { | |
| insertFront(values); | |
| } | |
| /** | |
| Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | |
| */ | |
| this(Stuff)(Stuff stuff) | |
| if (isInputRange!Stuff | |
| && isImplicitlyConvertible!(ElementType!Stuff, T) | |
| && !is(Stuff == T[])) | |
| { | |
| insertFront(stuff); | |
| } | |
| /** | |
| Comparison for equality. | |
| Complexity: $(BIGOH min(n, n1)) where `n1` is the number of | |
| elements in `rhs`. | |
| */ | |
| bool opEquals(const SList rhs) const | |
| { | |
| return opEquals(rhs); | |
| } | |
| /// ditto | |
| bool opEquals(ref const SList rhs) const | |
| { | |
| if (_root is rhs._root) return true; | |
| if (_root is null) return rhs._root is null || rhs._first is null; | |
| if (rhs._root is null) return _root is null || _first is null; | |
| const(Node) * n1 = _first, n2 = rhs._first; | |
| for (;; n1 = n1._next, n2 = n2._next) | |
| { | |
| if (!n1) return !n2; | |
| if (!n2 || n1._payload != n2._payload) return false; | |
| } | |
| } | |
| /** | |
| Defines the container's primary range, which embodies a forward range. | |
| */ | |
| struct Range | |
| { | |
| private Node * _head; | |
| private this(Node * p) { _head = p; } | |
| /// Input range primitives. | |
| @property bool empty() const { return !_head; } | |
| /// ditto | |
| @property ref T front() | |
| { | |
| assert(!empty, "SList.Range.front: Range is empty"); | |
| return _head._payload; | |
| } | |
| /// ditto | |
| void popFront() | |
| { | |
| assert(!empty, "SList.Range.popFront: Range is empty"); | |
| _head = _head._next; | |
| } | |
| /// Forward range primitive. | |
| @property Range save() { return this; } | |
| T moveFront() | |
| { | |
| import std.algorithm.mutation : move; | |
| assert(!empty, "SList.Range.moveFront: Range is empty"); | |
| return move(_head._payload); | |
| } | |
| bool sameHead(Range rhs) | |
| { | |
| return _head && _head == rhs._head; | |
| } | |
| } | |
| @safe unittest | |
| { | |
| static assert(isForwardRange!Range); | |
| } | |
| /** | |
| Property returning `true` if and only if the container has no | |
| elements. | |
| Complexity: $(BIGOH 1) | |
| */ | |
| @property bool empty() const | |
| { | |
| return _root is null || _first is null; | |
| } | |
| /** | |
| Duplicates the container. The elements themselves are not transitively | |
| duplicated. | |
| Complexity: $(BIGOH n). | |
| */ | |
| @property SList dup() | |
| { | |
| return SList(this[]); | |
| } | |
| /** | |
| Returns a range that iterates over all elements of the container, in | |
| forward order. | |
| Complexity: $(BIGOH 1) | |
| */ | |
| Range opSlice() | |
| { | |
| if (empty) | |
| return Range(null); | |
| else | |
| return Range(_first); | |
| } | |
| /** | |
| Forward to `opSlice().front`. | |
| Complexity: $(BIGOH 1) | |
| */ | |
| @property ref T front() | |
| { | |
| assert(!empty, "SList.front: List is empty"); | |
| return _first._payload; | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int(1, 2, 3); | |
| s.front = 42; | |
| assert(s == SList!int(42, 2, 3)); | |
| } | |
| /** | |
| Returns a new `SList` that's the concatenation of `this` and its | |
| argument. `opBinaryRight` is only defined if `Stuff` does not | |
| define `opBinary`. | |
| */ | |
| SList opBinary(string op, Stuff)(Stuff rhs) | |
| if (op == "~" && is(typeof(SList(rhs)))) | |
| { | |
| import std.range : chain, only; | |
| static if (isInputRange!Stuff) | |
| alias r = rhs; | |
| else | |
| auto r = only(rhs); | |
| return SList(this[].chain(r)); | |
| } | |
| /// ditto | |
| SList opBinaryRight(string op, Stuff)(Stuff lhs) | |
| if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs)))) | |
| { | |
| import std.range : chain, only; | |
| static if (isInputRange!Stuff) | |
| alias r = lhs; | |
| else | |
| auto r = only(lhs); | |
| return SList(r.chain(this[])); | |
| } | |
| /** | |
| Removes all contents from the `SList`. | |
| Postcondition: `empty` | |
| Complexity: $(BIGOH 1) | |
| */ | |
| void clear() | |
| { | |
| if (_root) | |
| _first = null; | |
| } | |
| /** | |
| Reverses SList in-place. Performs no memory allocation. | |
| Complexity: $(BIGOH n) | |
| */ | |
| void reverse() | |
| { | |
| if (!empty) | |
| { | |
| Node* prev; | |
| while (_first) | |
| { | |
| auto next = _first._next; | |
| _first._next = prev; | |
| prev = _first; | |
| _first = next; | |
| } | |
| _first = prev; | |
| } | |
| } | |
| /** | |
| Inserts `stuff` to the front of the container. `stuff` can be a | |
| value convertible to `T` or a range of objects convertible to $(D | |
| T). The stable version behaves the same, but guarantees that ranges | |
| iterating over the container are never invalidated. | |
| Returns: The number of elements inserted | |
| Complexity: $(BIGOH m), where `m` is the length of `stuff` | |
| */ | |
| size_t insertFront(Stuff)(Stuff stuff) | |
| if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) | |
| { | |
| initialize(); | |
| return insertAfterNode(_root, stuff); | |
| } | |
| /// ditto | |
| alias insert = insertFront; | |
| /// ditto | |
| alias stableInsert = insert; | |
| /// ditto | |
| alias stableInsertFront = insertFront; | |
| /** | |
| Picks one value in an unspecified position in the container, removes | |
| it from the container, and returns it. The stable version behaves the same, | |
| but guarantees that ranges iterating over the container are never invalidated. | |
| Precondition: `!empty` | |
| Returns: The element removed. | |
| Complexity: $(BIGOH 1). | |
| */ | |
| T removeAny() | |
| { | |
| import std.algorithm.mutation : move; | |
| assert(!empty, "SList.removeAny: List is empty"); | |
| auto result = move(_first._payload); | |
| _first = _first._next; | |
| return result; | |
| } | |
| /// ditto | |
| alias stableRemoveAny = removeAny; | |
| /** | |
| Removes the value at the front of the container. The stable version | |
| behaves the same, but guarantees that ranges iterating over the | |
| container are never invalidated. | |
| Precondition: `!empty` | |
| Complexity: $(BIGOH 1). | |
| */ | |
| void removeFront() | |
| { | |
| assert(!empty, "SList.removeFront: List is empty"); | |
| _first = _first._next; | |
| } | |
| /// ditto | |
| alias stableRemoveFront = removeFront; | |
| /** | |
| Removes `howMany` values at the front or back of the | |
| container. Unlike the unparameterized versions above, these functions | |
| do not throw if they could not remove `howMany` elements. Instead, | |
| if $(D howMany > n), all elements are removed. The returned value is | |
| the effective number of elements removed. The stable version behaves | |
| the same, but guarantees that ranges iterating over the container are | |
| never invalidated. | |
| Returns: The number of elements removed | |
| Complexity: $(BIGOH howMany * log(n)). | |
| */ | |
| size_t removeFront(size_t howMany) | |
| { | |
| size_t result; | |
| while (_first && result < howMany) | |
| { | |
| _first = _first._next; | |
| ++result; | |
| } | |
| return result; | |
| } | |
| /// ditto | |
| alias stableRemoveFront = removeFront; | |
| /** | |
| Inserts `stuff` after range `r`, which must be a range | |
| previously extracted from this container. Given that all ranges for a | |
| list end at the end of the list, this function essentially appends to | |
| the list and uses `r` as a potentially fast way to reach the last | |
| node in the list. Ideally `r` is positioned near or at the last | |
| element of the list. | |
| `stuff` can be a value convertible to `T` or a range of objects | |
| convertible to `T`. The stable version behaves the same, but | |
| guarantees that ranges iterating over the container are never | |
| invalidated. | |
| Returns: The number of values inserted. | |
| Complexity: $(BIGOH k + m), where `k` is the number of elements in | |
| `r` and `m` is the length of `stuff`. | |
| Example: | |
| -------------------- | |
| auto sl = SList!string(["a", "b", "d"]); | |
| sl.insertAfter(sl[], "e"); // insert at the end (slowest) | |
| assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"])); | |
| sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b" | |
| assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"])); | |
| -------------------- | |
| */ | |
| size_t insertAfter(Stuff)(Range r, Stuff stuff) | |
| if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) | |
| { | |
| initialize(); | |
| if (!_first) | |
| { | |
| enforce(!r._head); | |
| return insertFront(stuff); | |
| } | |
| enforce(r._head); | |
| auto n = findLastNode(r._head); | |
| return insertAfterNode(n, stuff); | |
| } | |
| /** | |
| Similar to `insertAfter` above, but accepts a range bounded in | |
| count. This is important for ensuring fast insertions in the middle of | |
| the list. For fast insertions after a specified position `r`, use | |
| $(D insertAfter(take(r, 1), stuff)). The complexity of that operation | |
| only depends on the number of elements in `stuff`. | |
| Precondition: $(D r.original.empty || r.maxLength > 0) | |
| Returns: The number of values inserted. | |
| Complexity: $(BIGOH k + m), where `k` is the number of elements in | |
| `r` and `m` is the length of `stuff`. | |
| */ | |
| size_t insertAfter(Stuff)(Take!Range r, Stuff stuff) | |
| if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T)) | |
| { | |
| auto orig = r.source; | |
| if (!orig._head) | |
| { | |
| // Inserting after a null range counts as insertion to the | |
| // front | |
| return insertFront(stuff); | |
| } | |
| enforce(!r.empty); | |
| // Find the last valid element in the range | |
| foreach (i; 1 .. r.maxLength) | |
| { | |
| if (!orig._head._next) break; | |
| orig.popFront(); | |
| } | |
| // insert here | |
| return insertAfterNode(orig._head, stuff); | |
| } | |
| /// ditto | |
| alias stableInsertAfter = insertAfter; | |
| /** | |
| Removes a range from the list in linear time. | |
| Returns: An empty range. | |
| Complexity: $(BIGOH n) | |
| */ | |
| Range linearRemove(Range r) | |
| { | |
| if (!_first) | |
| { | |
| enforce(!r._head); | |
| return this[]; | |
| } | |
| auto n = findNode(_root, r._head); | |
| n._next = null; | |
| return Range(null); | |
| } | |
| /** | |
| Removes a `Take!Range` from the list in linear time. | |
| Returns: A range comprehending the elements after the removed range. | |
| Complexity: $(BIGOH n) | |
| */ | |
| Range linearRemove(Take!Range r) | |
| { | |
| auto orig = r.source; | |
| // We have something to remove here | |
| if (orig._head == _first) | |
| { | |
| // remove straight from the head of the list | |
| for (; !r.empty; r.popFront()) | |
| { | |
| removeFront(); | |
| } | |
| return this[]; | |
| } | |
| if (!r.maxLength) | |
| { | |
| // Nothing to remove, return the range itself | |
| return orig; | |
| } | |
| // Remove from somewhere in the middle of the list | |
| enforce(_first); | |
| auto n1 = findNode(_root, orig._head); | |
| auto n2 = findLastNode(orig._head, r.maxLength); | |
| n1._next = n2._next; | |
| return Range(n1._next); | |
| } | |
| /// ditto | |
| alias stableLinearRemove = linearRemove; | |
| /** | |
| Removes the first occurence of an element from the list in linear time. | |
| Returns: True if the element existed and was successfully removed, false otherwise. | |
| Params: | |
| value = value of the node to be removed | |
| Complexity: $(BIGOH n) | |
| */ | |
| bool linearRemoveElement(T value) | |
| { | |
| auto n1 = findNodeByValue(_root, value); | |
| if (n1 && n1._next) | |
| { | |
| n1._next = n1._next._next; | |
| return true; | |
| } | |
| return false; | |
| } | |
| } | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| auto e = SList!int(); | |
| auto b = e.linearRemoveElement(2); | |
| assert(b == false); | |
| assert(e.empty()); | |
| auto a = SList!int(-1, 1, 2, 1, 3, 4); | |
| b = a.linearRemoveElement(1); | |
| assert(equal(a[], [-1, 2, 1, 3, 4])); | |
| assert(b == true); | |
| b = a.linearRemoveElement(-1); | |
| assert(b == true); | |
| assert(equal(a[], [2, 1, 3, 4])); | |
| b = a.linearRemoveElement(1); | |
| assert(b == true); | |
| assert(equal(a[], [2, 3, 4])); | |
| b = a.linearRemoveElement(2); | |
| assert(b == true); | |
| b = a.linearRemoveElement(20); | |
| assert(b == false); | |
| assert(equal(a[], [3, 4])); | |
| b = a.linearRemoveElement(4); | |
| assert(b == true); | |
| assert(equal(a[], [3])); | |
| b = a.linearRemoveElement(3); | |
| assert(b == true); | |
| assert(a.empty()); | |
| a.linearRemoveElement(3); | |
| } | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| auto a = SList!int(5); | |
| auto b = a; | |
| auto r = a[]; | |
| a.insertFront(1); | |
| b.insertFront(2); | |
| assert(equal(a[], [2, 1, 5])); | |
| assert(equal(b[], [2, 1, 5])); | |
| r.front = 9; | |
| assert(equal(a[], [2, 1, 9])); | |
| assert(equal(b[], [2, 1, 9])); | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int(1, 2, 3); | |
| auto n = s.findLastNode(s._root); | |
| assert(n && n._payload == 3); | |
| } | |
| @safe unittest | |
| { | |
| import std.range.primitives; | |
| auto s = SList!int(1, 2, 5, 10); | |
| assert(walkLength(s[]) == 4); | |
| } | |
| @safe unittest | |
| { | |
| import std.range : take; | |
| auto src = take([0, 1, 2, 3], 3); | |
| auto s = SList!int(src); | |
| assert(s == SList!int(0, 1, 2)); | |
| } | |
| @safe unittest | |
| { | |
| auto a = SList!int(); | |
| auto b = SList!int(); | |
| auto c = a ~ b[]; | |
| assert(c.empty); | |
| } | |
| @safe unittest | |
| { | |
| auto a = SList!int(1, 2, 3); | |
| auto b = SList!int(4, 5, 6); | |
| auto c = a ~ b[]; | |
| assert(c == SList!int(1, 2, 3, 4, 5, 6)); | |
| } | |
| @safe unittest | |
| { | |
| auto a = SList!int(1, 2, 3); | |
| auto b = [4, 5, 6]; | |
| auto c = a ~ b; | |
| assert(c == SList!int(1, 2, 3, 4, 5, 6)); | |
| } | |
| @safe unittest | |
| { | |
| auto a = SList!int(1, 2, 3); | |
| auto c = a ~ 4; | |
| assert(c == SList!int(1, 2, 3, 4)); | |
| } | |
| @safe unittest | |
| { | |
| auto a = SList!int(2, 3, 4); | |
| auto b = 1 ~ a; | |
| assert(b == SList!int(1, 2, 3, 4)); | |
| } | |
| @safe unittest | |
| { | |
| auto a = [1, 2, 3]; | |
| auto b = SList!int(4, 5, 6); | |
| auto c = a ~ b; | |
| assert(c == SList!int(1, 2, 3, 4, 5, 6)); | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int(1, 2, 3, 4); | |
| s.insertFront([ 42, 43 ]); | |
| assert(s == SList!int(42, 43, 1, 2, 3, 4)); | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int(1, 2, 3); | |
| assert(s.removeAny() == 1); | |
| assert(s == SList!int(2, 3)); | |
| assert(s.stableRemoveAny() == 2); | |
| assert(s == SList!int(3)); | |
| } | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| auto s = SList!int(1, 2, 3); | |
| s.removeFront(); | |
| assert(equal(s[], [2, 3])); | |
| s.stableRemoveFront(); | |
| assert(equal(s[], [3])); | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int(1, 2, 3, 4, 5, 6, 7); | |
| assert(s.removeFront(3) == 3); | |
| assert(s == SList!int(4, 5, 6, 7)); | |
| } | |
| @safe unittest | |
| { | |
| auto a = SList!int(1, 2, 3); | |
| auto b = SList!int(1, 2, 3); | |
| assert(a.insertAfter(a[], b[]) == 3); | |
| } | |
| @safe unittest | |
| { | |
| import std.range : take; | |
| auto s = SList!int(1, 2, 3, 4); | |
| auto r = take(s[], 2); | |
| assert(s.insertAfter(r, 5) == 1); | |
| assert(s == SList!int(1, 2, 5, 3, 4)); | |
| } | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.range : take; | |
| // insertAfter documentation example | |
| auto sl = SList!string(["a", "b", "d"]); | |
| sl.insertAfter(sl[], "e"); // insert at the end (slowest) | |
| assert(equal(sl[], ["a", "b", "d", "e"])); | |
| sl.insertAfter(take(sl[], 2), "c"); // insert after "b" | |
| assert(equal(sl[], ["a", "b", "c", "d", "e"])); | |
| } | |
| @safe unittest | |
| { | |
| import std.range.primitives; | |
| auto s = SList!int(1, 2, 3, 4, 5); | |
| auto r = s[]; | |
| popFrontN(r, 3); | |
| auto r1 = s.linearRemove(r); | |
| assert(s == SList!int(1, 2, 3)); | |
| assert(r1.empty); | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int(1, 2, 3, 4, 5); | |
| auto r = s[]; | |
| auto r1 = s.linearRemove(r); | |
| assert(s == SList!int()); | |
| assert(r1.empty); | |
| } | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.range; | |
| auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); | |
| auto r = s[]; | |
| popFrontN(r, 3); | |
| auto r1 = take(r, 4); | |
| assert(equal(r1, [4, 5, 6, 7])); | |
| auto r2 = s.linearRemove(r1); | |
| assert(s == SList!int(1, 2, 3, 8, 9, 10)); | |
| assert(equal(r2, [8, 9, 10])); | |
| } | |
| @safe unittest | |
| { | |
| import std.range.primitives; | |
| auto lst = SList!int(1, 5, 42, 9); | |
| assert(!lst.empty); | |
| assert(lst.front == 1); | |
| assert(walkLength(lst[]) == 4); | |
| auto lst2 = lst ~ [ 1, 2, 3 ]; | |
| assert(walkLength(lst2[]) == 7); | |
| auto lst3 = lst ~ [ 7 ]; | |
| assert(walkLength(lst3[]) == 5); | |
| } | |
| @safe unittest | |
| { | |
| auto s = make!(SList!int)(1, 2, 3); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=5193 | |
| @safe unittest | |
| { | |
| static struct Data | |
| { | |
| const int val; | |
| } | |
| SList!Data list; | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int([1, 2, 3]); | |
| s.front = 5; //test frontAssign | |
| assert(s.front == 5); | |
| auto r = s[]; | |
| r.front = 1; //test frontAssign | |
| assert(r.front == 1); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=14920 | |
| @safe unittest | |
| { | |
| SList!int s; | |
| s.insertAfter(s[], 1); | |
| assert(s.front == 1); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=15659 | |
| @safe unittest | |
| { | |
| SList!int s; | |
| s.clear(); | |
| } | |
| @safe unittest | |
| { | |
| SList!int s; | |
| s.reverse(); | |
| } | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| auto s = SList!int([1, 2, 3]); | |
| assert(s[].equal([1, 2, 3])); | |
| s.reverse(); | |
| assert(s[].equal([3, 2, 1])); | |
| } | |
| @safe unittest | |
| { | |
| auto s = SList!int([4, 6, 8, 12, 16]); | |
| auto d = s.dup; | |
| assert(d !is s); | |
| assert(d == s); | |
| } |