summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-05-15 19:50:20 +0200
committerEugen Wissner <belka@caraus.de>2017-05-15 19:50:20 +0200
commit357c7e279d1714fd32d990a933b97420a8b94f51 (patch)
treebb0290c57a2fa2f1becf041346064604c1426817 /source
parent32e19c8b582e27d097004abe5386f45d4b558ea4 (diff)
downloadtanya-357c7e279d1714fd32d990a933b97420a8b94f51.tar.gz
Add doubly-linked list
DList is an adjusted copy of SList. Missing: * insertBack * insertAfter * removeBack
Diffstat (limited to 'source')
-rw-r--r--source/tanya/container/entry.d13
-rw-r--r--source/tanya/container/list.d972
2 files changed, 943 insertions, 42 deletions
diff --git a/source/tanya/container/entry.d b/source/tanya/container/entry.d
index cc74204..a455701 100644
--- a/source/tanya/container/entry.d
+++ b/source/tanya/container/entry.d
@@ -14,9 +14,18 @@ module tanya.container.entry;
package struct SEntry(T)
{
- /// Item content.
+ // Item content.
T content;
- /// Next item.
+ // Next item.
SEntry* next;
}
+
+package struct DEntry(T)
+{
+ // Item content.
+ T content;
+
+ // Previous and next item.
+ DEntry* next, prev;
+}
diff --git a/source/tanya/container/list.d b/source/tanya/container/list.d
index d0946d9..942fc3e 100644
--- a/source/tanya/container/list.d
+++ b/source/tanya/container/list.d
@@ -34,7 +34,7 @@ struct SRange(E)
invariant
{
- assert(head !is null);
+ assert(this.head !is null);
}
private this(ref EntryPointer head) @trusted
@@ -49,14 +49,9 @@ struct SRange(E)
return this;
}
- @property size_t length() const
- {
- return count(opIndex());
- }
-
@property bool empty() const
{
- return *head is null;
+ return *this.head is null;
}
@property ref inout(E) front() inout
@@ -66,7 +61,7 @@ struct SRange(E)
}
body
{
- return (*head).content;
+ return (*this.head).content;
}
void popFront() @trusted
@@ -76,17 +71,17 @@ struct SRange(E)
}
body
{
- head = &(*head).next;
+ this.head = &(*this.head).next;
}
SRange opIndex()
{
- return typeof(return)(*head);
+ return typeof(return)(*this.head);
}
SRange!(const E) opIndex() const
{
- return typeof(return)(*head);
+ return typeof(return)(*this.head);
}
}
@@ -152,20 +147,16 @@ struct SList(T)
this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
{
this(allocator);
+ if (len == 0)
+ {
+ return;
+ }
- Entry* next;
- foreach (i; 0 .. len)
+ Entry* next = this.head = allocator.make!Entry(init);
+ foreach (i; 1 .. len)
{
- if (next is null)
- {
- next = allocator.make!Entry(init);
- head = next;
- }
- else
- {
- next.next = allocator.make!Entry(init);
- next = next.next;
- }
+ next.next = allocator.make!Entry(init);
+ next = next.next;
}
}
@@ -232,7 +223,7 @@ struct SList(T)
this(allocator);
if (allocator is init.allocator)
{
- head = init.head;
+ this.head = init.head;
init.head = null;
}
else
@@ -240,10 +231,10 @@ struct SList(T)
Entry* next;
for (auto current = init.head; current !is null; current = current.next)
{
- if (head is null)
+ if (this.head is null)
{
- head = allocator.make!Entry(move(current.content));
- next = head;
+ this.head = allocator.make!Entry(move(current.content));
+ next = this.head;
}
else
{
@@ -319,7 +310,7 @@ struct SList(T)
}
body
{
- return head.content;
+ return this.head.content;
}
private size_t moveEntry(R)(ref Entry* head, ref R el) @trusted
@@ -346,14 +337,14 @@ struct SList(T)
size_t insertFront(R)(R el)
if (isImplicitlyConvertible!(R, T))
{
- return moveEntry(head, el);
+ return moveEntry(this.head, el);
}
/// Ditto.
size_t insertFront(R)(ref R el) @trusted
if (isImplicitlyConvertible!(R, T))
{
- head = allocator.make!Entry(el, head);
+ this.head = allocator.make!Entry(el, this.head);
return 1;
}
@@ -381,8 +372,8 @@ struct SList(T)
}
if (newHead !is null)
{
- next.next = head;
- head = newHead;
+ next.next = this.head;
+ this.head = newHead;
}
return retLength;
}
@@ -419,8 +410,8 @@ struct SList(T)
{
private bool checkRangeBelonging(ref SRange!T r) const
{
- const(Entry*)* pos;
- for (pos = &head; pos != r.head && *pos !is null; pos = &(*pos).next)
+ const(Entry*)* pos = &this.head;
+ for (; pos != r.head && *pos !is null; pos = &(*pos).next)
{
}
return pos == r.head;
@@ -595,7 +586,7 @@ struct SList(T)
*/
@property bool empty() const
{
- return head is null;
+ return this.head is null;
}
/**
@@ -687,7 +678,7 @@ struct SList(T)
}
body
{
- typeof(this) outOfScopeList;
+ auto outOfScopeList = typeof(this)(allocator);
outOfScopeList.head = *r.head;
*r.head = null;
@@ -713,13 +704,13 @@ struct SList(T)
*/
SRange!T opIndex()
{
- return typeof(return)(head);
+ return typeof(return)(this.head);
}
/// Ditto.
SRange!(const T) opIndex() const
{
- return typeof(return)(head);
+ return typeof(return)(this.head);
}
/**
@@ -766,7 +757,7 @@ struct SList(T)
&& isInputRange!R
&& isImplicitlyConvertible!(ElementType!R, T))
{
- Entry** next = &head;
+ Entry** next = &this.head;
foreach (ref e; that)
{
@@ -817,7 +808,6 @@ struct SList(T)
assert(l1 == l2);
}
-
mixin DefaultAllocator;
}
@@ -865,3 +855,905 @@ private @nogc @safe unittest
++i;
}
}
+
+/**
+ * Forward range for the $(D_PSYMBOL SList).
+ *
+ * Params:
+ * E = Element type.
+ */
+struct DRange(E)
+{
+ private alias EntryPointer = CopyConstness!(E, DEntry!(Unqual!E)*);
+ private alias TailPointer = CopyConstness!(E, DEntry!(Unqual!E))*;
+
+ private EntryPointer* head;
+ private TailPointer tail;
+
+ invariant
+ {
+ assert(this.head !is null);
+ }
+
+ private this(ref EntryPointer head, TailPointer tail) @trusted
+ {
+ this.head = &head;
+ this.tail = tail;
+ }
+
+ @disable this();
+
+ @property DRange save()
+ {
+ return this;
+ }
+
+ @property bool empty() const
+ {
+ return *this.head is null || *this.head is this.tail.next;
+ }
+
+ @property ref inout(E) front() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return (*this.head).content;
+ }
+
+ @property ref inout(E) back() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return this.tail.content;
+ }
+
+ void popFront() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ this.head = &(*this.head).next;
+ }
+
+ void popBack() @trusted
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ this.tail = this.tail.prev;
+ }
+
+ DRange opIndex()
+ {
+ return typeof(return)(*this.head, this.tail);
+ }
+
+ DRange!(const E) opIndex() const
+ {
+ return typeof(return)(*this.head, this.tail);
+ }
+}
+
+/**
+ * Doubly-linked list.
+ *
+ * Params:
+ * T = Content type.
+ */
+struct DList(T)
+{
+ private alias Entry = DEntry!T;
+
+ // 0th and the last elements of the list.
+ private Entry* head, tail;
+
+ invariant
+ {
+ assert((this.tail is null && this.head is null)
+ || (this.tail !is null && this.head !is null));
+ assert(this.tail is null || this.tail.next is null);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL DList) with the elements from a static array.
+ *
+ * Params:
+ * R = Static array size.
+ * init = Values to initialize the list with.
+ * allocator = Allocator.
+ */
+ this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator)
+ {
+ this(allocator);
+ insertFront(init[]);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l = DList!int([5, 8, 15]);
+ assert(l.front == 5);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL DList) with the elements from an input range.
+ *
+ * Params:
+ * R = Type of the initial range.
+ * init = Values to initialize the list with.
+ * allocator = Allocator.
+ */
+ this(R)(R init, shared Allocator allocator = defaultAllocator)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ this(allocator);
+ insertFront(init);
+ }
+
+ /**
+ * Creates a new $(D_PSYMBOL DList).
+ *
+ * Params:
+ * len = Initial length of the list.
+ * init = Initial value to fill the list with.
+ * allocator = Allocator.
+ */
+ this(const size_t len, T init, shared Allocator allocator = defaultAllocator) @trusted
+ {
+ this(allocator);
+ if (len == 0)
+ {
+ return;
+ }
+
+ Entry* next = this.head = allocator.make!Entry(init);
+ foreach (i; 1 .. len)
+ {
+ next.next = allocator.make!Entry(init);
+ next.next.prev = next;
+ next = next.next;
+ }
+ this.tail = next;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l = DList!int(2, 3);
+ assert(l.length == 2);
+ assert(l.front == 3);
+ assert(l.back == 3);
+ }
+
+ /// Ditto.
+ this(const size_t len, shared Allocator allocator = defaultAllocator)
+ {
+ this(len, T.init, allocator);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l = DList!int(2);
+ assert(l.length == 2);
+ assert(l.front == 0);
+ }
+
+ /// Ditto.
+ this(shared Allocator allocator)
+ in
+ {
+ assert(allocator !is null);
+ }
+ body
+ {
+ this.allocator_ = allocator;
+ }
+
+ /**
+ * Initializes this list from another one.
+ *
+ * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
+ * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
+ * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
+ * storage, otherwise, the storage will be allocated with
+ * $(D_PARAM allocator) and all elements will be moved;
+ * $(D_PARAM init) will be destroyed at the end.
+ *
+ * If $(D_PARAM init) is passed by reference, it will be copied.
+ *
+ * Params:
+ * R = Source list type.
+ * init = Source list.
+ * allocator = Allocator.
+ */
+ this(R)(ref R init, shared Allocator allocator = defaultAllocator)
+ if (is(Unqual!R == DList))
+ {
+ this(init[], allocator);
+ }
+
+ /// Ditto.
+ this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
+ if (is(R == DList))
+ {
+ this(allocator);
+ if (allocator is init.allocator)
+ {
+ this.head = init.head;
+ this.tail = init.tail;
+ init.head = this.tail = null;
+ }
+ else
+ {
+ Entry* next;
+ for (auto current = init.head; current !is null; current = current.next)
+ {
+ if (this.head is null)
+ {
+ this.head = allocator.make!Entry(move(current.content));
+ next = this.head;
+ }
+ else
+ {
+ next.next = allocator.make!Entry(move(current.content));
+ next.next.prev = next;
+ next = next.next;
+ }
+ }
+ this.tail = next;
+ }
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([5, 1, 234]);
+ auto l2 = DList!int(l1);
+ assert(l1 == l2);
+ }
+
+ /**
+ * Removes all elements from the list.
+ */
+ ~this()
+ {
+ clear();
+ }
+
+ /**
+ * Copies the list.
+ */
+ this(this)
+ {
+ auto list = typeof(this)(this[], this.allocator);
+ this.head = list.head;
+ this.tail = list.tail;
+ list.head = list .tail = null;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([5, 1, 234]);
+ auto l2 = l1;
+ assert(l1 == l2);
+ }
+
+ /**
+ * Removes all contents from the list.
+ */
+ void clear()
+ {
+ while (!empty)
+ {
+ removeFront();
+ }
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ DList!int l = DList!int([8, 5]);
+
+ assert(!l.empty);
+ l.clear();
+ assert(l.empty);
+ }
+
+ /**
+ * Returns: First element.
+ */
+ @property ref inout(T) front() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return this.head.content;
+ }
+
+ /**
+ * Returns: Last element.
+ */
+ @property ref inout(T) back() inout
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ return this.tail.content;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l = DList!int([25]);
+ assert(l.front == 25);
+ assert(l.back == 25);
+
+ l.insertFront(30);
+ assert(l.front == 30);
+ assert(l.back == 25);
+ }
+
+ private size_t moveEntry(R)(ref Entry* head, ref R el) @trusted
+ if (isImplicitlyConvertible!(R, T))
+ {
+ auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
+
+ el.moveEmplace(temp.content);
+ temp.next = head;
+ temp.prev = null;
+ setTailIfNull(temp);
+
+ head = temp;
+ return 1;
+ }
+
+ private void setTailIfNull(ref Entry* tail = this.tail)
+ {
+ if (this.tail is null)
+ {
+ this.tail = tail;
+ }
+ }
+
+ /**
+ * Inserts a new element at the beginning.
+ *
+ * Params:
+ * R = Type of the inserted value(s).
+ * el = New element(s).
+ *
+ * Returns: The number of elements inserted.
+ */
+ size_t insertFront(R)(R el)
+ if (isImplicitlyConvertible!(R, T))
+ {
+ return moveEntry(this.head, el);
+ }
+
+ /// Ditto.
+ size_t insertFront(R)(ref R el) @trusted
+ if (isImplicitlyConvertible!(R, T))
+ {
+ this.head = allocator.make!Entry(el, this.head);
+ setTailIfNull();
+ return 1;
+ }
+
+ /// Ditto.
+ size_t insertFront(R)(R el) @trusted
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ size_t retLength;
+ Entry* next, newHead;
+
+ if (!el.empty)
+ {
+ next = allocator.make!Entry(el.front);
+ newHead = next;
+ el.popFront();
+ retLength = 1;
+ }
+ foreach (ref e; el)
+ {
+ next.next = allocator.make!Entry(e);
+ next = next.next;
+ ++retLength;
+ }
+ if (this.head is null)
+ {
+ this.tail = next;
+ }
+ if (newHead !is null)
+ {
+ next.next = this.head;
+ this.head = newHead;
+ }
+
+ return retLength;
+ }
+
+ /// Ditto.
+ size_t insertFront(size_t R)(T[R] el)
+ {
+ return insertFront!(T[])(el[]);
+ }
+
+ /// Ditto.
+ alias insert = insertFront;
+
+ ///
+ @safe @nogc unittest
+ {
+ DList!int l1;
+
+ assert(l1.insertFront(8) == 1);
+ assert(l1.front == 8);
+ assert(l1.insertFront(9) == 1);
+ assert(l1.front == 9);
+
+ DList!int l2;
+ assert(l2.insertFront([25, 30, 15]) == 3);
+ assert(l2.front == 25);
+
+ l2.insertFront(l1[]);
+ assert(l2.length == 5);
+ assert(l2.front == 9);
+ }
+
+ version (assert)
+ {
+ private bool checkRangeBelonging(ref DRange!T r) const
+ {
+ const(Entry*)* pos = &this.head;
+ for (; pos != r.head && *pos !is null; pos = &(*pos).next)
+ {
+ }
+ return pos == r.head;
+ }
+ }
+
+ /**
+ * Inserts new elements before $(D_PARAM r).
+ *
+ * Params:
+ * R = Type of the inserted value(s).
+ * r = Range extracted from this list.
+ * el = New element(s).
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Precondition: $(D_PARAM r) is extracted from this list.
+ */
+ size_t insertBefore(R)(DRange!T r, R el)
+ if (isImplicitlyConvertible!(R, T))
+ in
+ {
+ assert(checkRangeBelonging(r));
+ }
+ body
+ {
+ return moveEntry(*r.head, el);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([234, 5, 1]);
+ auto l2 = DList!int([5, 1]);
+ l2.insertBefore(l2[], 234);
+ assert(l1 == l2);
+ }
+
+ /// Ditto.
+ size_t insertBefore(R)(DRange!T r, R el)
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ in
+ {
+ assert(checkRangeBelonging(r));
+ }
+ body
+ {
+ size_t inserted;
+ foreach (e; el)
+ {
+ inserted += insertBefore(r, e);
+ r.popFront();
+ }
+ return inserted;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([5, 234, 30, 1]);
+ auto l2 = DList!int([5, 1]);
+ auto l3 = DList!int([234, 30]);
+ auto r = l2[];
+ r.popFront();
+ l2.insertBefore(r, l3[]);
+ assert(l1 == l2);
+ }
+
+ /// Ditto.
+ size_t insertBefore(DRange!T r, ref T el) @trusted
+ in
+ {
+ assert(checkRangeBelonging(r));
+ }
+ body
+ {
+ *r.head = allocator.make!Entry(el, *r.head);
+ return 1;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([234, 5, 1]);
+ auto l2 = DList!int([5, 1]);
+ int var = 234;
+ l2.insertBefore(l2[], var);
+ assert(l1 == l2);
+ }
+
+ /**
+ * Inserts elements from a static array before $(D_PARAM r).
+ *
+ * Params:
+ * R = Static array size.
+ * r = Range extracted from this list.
+ * el = New elements.
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Precondition: $(D_PARAM r) is extracted from this list.
+ */
+ size_t insertBefore(size_t R)(DRange!T r, T[R] el)
+ {
+ return insertFront!(T[])(el[]);
+ }
+
+ /**
+ * Returns: How many elements are in the list.
+ */
+ @property size_t length() const
+ {
+ return count(this[]);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ DList!int l;
+
+ l.insertFront(8);
+ l.insertFront(9);
+ assert(l.length == 2);
+ l.removeFront();
+ assert(l.length == 1);
+ l.removeFront();
+ assert(l.length == 0);
+ }
+
+ /**
+ * Comparison for equality.
+ *
+ * Params:
+ * that = The list to compare with.
+ *
+ * Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false)
+ * otherwise.
+ */
+ bool opEquals()(auto ref typeof(this) that) inout
+ {
+ return equal(this[], that[]);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ DList!int l1, l2;
+
+ l1.insertFront(8);
+ l1.insertFront(9);
+ l2.insertFront(8);
+ l2.insertFront(10);
+ assert(l1 != l2);
+
+ l1.removeFront();
+ assert(l1 != l2);
+
+ l2.removeFront();
+ assert(l1 == l2);
+
+ l1.removeFront();
+ assert(l1 != l2);
+
+ l2.removeFront();
+ assert(l1 == l2);
+ }
+
+ /**
+ * Returns: $(D_KEYWORD true) if the list is empty.
+ */
+ @property bool empty() const
+ {
+ return this.head is null;
+ }
+
+ /**
+ * Returns the first element and moves to the next one.
+ *
+ * Returns: The first element.
+ *
+ * Precondition: $(D_INLINECODE !empty)
+ */
+ void removeFront()
+ in
+ {
+ assert(!empty);
+ }
+ body
+ {
+ auto n = this.head.next;
+
+ this.allocator.dispose(this.head);
+ this.head = n;
+ if (this.head is null)
+ {
+ this.tail = null;
+ }
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ DList!int l;
+
+ l.insertFront(8);
+ l.insertFront(9);
+ assert(l.front == 9);
+ l.removeFront();
+ assert(l.front == 8);
+ l.removeFront();
+ assert(l.empty);
+ }
+
+ /**
+ * Removes $(D_PARAM howMany) elements from the list.
+ *
+ * Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not
+ * remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is
+ * greater than the list length, all elements are removed.
+ *
+ * Params:
+ * howMany = How many elements should be removed.
+ *
+ * Returns: The number of elements removed.
+ */
+ size_t removeFront(const size_t howMany)
+ out (removed)
+ {
+ assert(removed <= howMany);
+ }
+ body
+ {
+ size_t i;
+ for (; i < howMany && !empty; ++i)
+ {
+ removeFront();
+ }
+ return i;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ DList!int l = DList!int([8, 5, 4]);
+
+ assert(l.removeFront(0) == 0);
+ assert(l.removeFront(2) == 2);
+ assert(l.removeFront(3) == 1);
+ assert(l.removeFront(3) == 0);
+ }
+
+ /**
+ * Removes $(D_PARAM r) from the list.
+ *
+ * Params:
+ * r = The range to remove.
+ *
+ * Returns: An empty range.
+ *
+ * Precondition: $(D_PARAM r) is extracted from this list.
+ */
+ DRange!T remove(DRange!T r)
+ in
+ {
+ assert(checkRangeBelonging(r));
+ }
+ body
+ {
+ auto outOfScopeList = typeof(this)(allocator);
+ outOfScopeList.head = *r.head;
+ outOfScopeList.tail = r.tail;
+ if (r.tail is null)
+ {
+ *r.head = null;
+ }
+ else
+ {
+ outOfScopeList.tail.next = null;
+ *r.head = r.tail.next;
+ }
+
+ return r;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([5, 234, 30, 1]);
+ auto l2 = DList!int([5]);
+ auto r = l1[];
+
+ r.popFront();
+
+ assert(l1.remove(r).empty);
+ assert(l1 == l2);
+ }
+
+ /**
+ * Returns: Range that iterates over all elements of the container, in
+ * forward order.
+ */
+ DRange!T opIndex()
+ {
+ return typeof(return)(this.head, this.tail);
+ }
+
+ /// Ditto.
+ DRange!(const T) opIndex() const
+ {
+ return typeof(return)(this.head, this.tail);
+ }
+
+ /**
+ * Assigns another list.
+ *
+ * If $(D_PARAM that) is passed by value, it won't be copied, but moved.
+ * This list will take the ownership over $(D_PARAM that)'s storage and
+ * the allocator.
+ *
+ * If $(D_PARAM that) is passed by reference, it will be copied.
+ *
+ * Params:
+ * R = Content type.
+ * that = The value should be assigned.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(R)(ref R that)
+ if (is(Unqual!R == DList))
+ {
+ return this = that[];
+ }
+
+ /// Ditto.
+ ref typeof(this) opAssign(R)(R that)
+ if (is(R == DList))
+ {
+ swap(this.head, that.head);
+ swap(this.tail, that.tail);
+ swap(this.allocator_, that.allocator_);
+ return this;
+ }
+
+ /**
+ * Assigns an input range.
+ *
+ * Params:
+ * R = Type of the initial range.
+ * that = Values to initialize the list with.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(R)(R that) @trusted
+ if (!isInfinite!R
+ && isInputRange!R
+ && isImplicitlyConvertible!(ElementType!R, T))
+ {
+ Entry** next = &this.head;
+
+ foreach (ref e; that)
+ {
+ if (*next is null)
+ {
+ *next = allocator.make!Entry(e);
+ }
+ else
+ {
+ (*next).content = e;
+ }
+ next = &(*next).next;
+ }
+ remove(DRange!T(*next, this.tail));
+
+ return this;
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([5, 4, 9]);
+ auto l2 = DList!int([9, 4]);
+ l1 = l2[];
+ assert(l1 == l2);
+ }
+
+ /**
+ * Assigns a static array.
+ *
+ * Params:
+ * R = Static array size.
+ * that = Values to initialize the vector with.
+ *
+ * Returns: $(D_KEYWORD this).
+ */
+ ref typeof(this) opAssign(size_t R)(T[R] that)
+ {
+ return opAssign!(T[])(that[]);
+ }
+
+ ///
+ @safe @nogc unittest
+ {
+ auto l1 = DList!int([5, 4, 9]);
+ auto l2 = DList!int([9, 4]);
+ l1 = [9, 4];
+ assert(l1 == l2);
+ }
+
+ mixin DefaultAllocator;
+}
+
+///
+@nogc unittest
+{
+ DList!int l;
+ size_t i;
+
+ l.insertFront(5);
+ l.insertFront(4);
+ l.insertFront(9);
+ foreach (e; l)
+ {
+ assert(i != 0 || e == 9);
+ assert(i != 1 || e == 4);
+ assert(i != 2 || e == 5);
+ ++i;
+ }
+ assert(i == 3);
+}