Skip to content

Commit

Permalink
Merge pull request #2401 from sigod/slist-correct-reference-semantics
Browse files Browse the repository at this point in the history
SList: correct reference semantics
  • Loading branch information
H. S. Teoh committed Aug 21, 2014
2 parents 8963d41 + b1e4004 commit 647385f
Showing 1 changed file with 73 additions and 35 deletions.
108 changes: 73 additions & 35 deletions std/container/slist.d
Expand Up @@ -5,17 +5,36 @@ public import std.container.util;

/**
Implements a simple and fast singly-linked list.
$(D SList) uses reference semantics.
*/
struct SList(T)
{
private struct Node
{
T _payload;
Node * _next;
this(T a, Node* b) { _payload = a; _next = b; }
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);
return _root._next;
}

private static Node * findLastNode(Node * n)
{
assert(n);
Expand Down Expand Up @@ -87,7 +106,11 @@ elements in $(D rhs).
/// ditto
bool opEquals(ref const SList rhs) const
{
const(Node) * n1 = _root, n2 = rhs._root;
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)
{
Expand Down Expand Up @@ -151,7 +174,7 @@ Complexity: $(BIGOH 1)
*/
@property bool empty() const
{
return _root is null;
return _root is null || _first is null;
}

/**
Expand All @@ -173,7 +196,10 @@ Complexity: $(BIGOH 1)
*/
Range opSlice()
{
return Range(_root);
if (empty)
return Range(null);
else
return Range(_first);
}

/**
Expand All @@ -184,7 +210,7 @@ Complexity: $(BIGOH 1)
@property ref T front()
{
assert(!empty, "SList.front: List is empty");
return _root._payload;
return _first._payload;
}

unittest
Expand All @@ -206,8 +232,8 @@ define $(D opBinary).
if (empty) return toAdd;
// TODO: optimize
auto result = dup;
auto n = findLastNode(result._root);
n._next = toAdd._root;
auto n = findLastNode(result._first);
n._next = toAdd._first;
return result;
}

Expand All @@ -231,7 +257,7 @@ Complexity: $(BIGOH 1)
*/
void clear()
{
_root = null;
_first = null;
}

/**
Expand All @@ -247,28 +273,30 @@ Complexity: $(BIGOH m), where $(D m) is the length of $(D stuff)
size_t insertFront(Stuff)(Stuff stuff)
if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
{
initialize();
size_t result;
Node * n, newRoot;
foreach (item; stuff)
{
auto newNode = new Node(item, null);
auto newNode = new Node(null, item);
(newRoot ? n._next : newRoot) = newNode;
n = newNode;
++result;
}
if (!n) return 0;
// Last node points to the old root
n._next = _root;
_root = newRoot;
n._next = _first;
_first = newRoot;
return result;
}

/// ditto
size_t insertFront(Stuff)(Stuff stuff)
if (isImplicitlyConvertible!(Stuff, T))
{
auto newRoot = new Node(stuff, _root);
_root = newRoot;
initialize();
auto newRoot = new Node(_first, stuff);
_first = newRoot;
return 1;
}

Expand Down Expand Up @@ -297,8 +325,8 @@ Complexity: $(BIGOH 1).
import std.algorithm : move;

assert(!empty, "SList.removeAny: List is empty");
auto result = move(_root._payload);
_root = _root._next;
auto result = move(_first._payload);
_first = _first._next;
return result;
}
/// ditto
Expand All @@ -316,7 +344,7 @@ Complexity: $(BIGOH 1).
void removeFront()
{
assert(!empty, "SList.removeFront: List is empty");
_root = _root._next;
_first = _first._next;
}

/// ditto
Expand All @@ -338,9 +366,9 @@ Complexity: $(BIGOH howMany * log(n)).
size_t removeFront(size_t howMany)
{
size_t result;
while (_root && result < howMany)
while (_first && result < howMany)
{
_root = _root._next;
_first = _first._next;
++result;
}
return result;
Expand Down Expand Up @@ -379,7 +407,7 @@ assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));

size_t insertAfter(Stuff)(Range r, Stuff stuff)
{
if (!_root)
if (!_first)
{
enforce(!r._head);
return insertFront(stuff);
Expand All @@ -388,7 +416,7 @@ assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));
auto n = findLastNode(r._head);
SList tmp;
auto result = tmp.insertFront(stuff);
n._next = tmp._root;
n._next = tmp._first;
return result;
}

Expand Down Expand Up @@ -424,9 +452,10 @@ $(D r) and $(D m) is the length of $(D stuff).
}
// insert here
SList tmp;
tmp._root = orig._head._next;
tmp._root = new Node();
tmp._first = orig._head._next;
auto result = tmp.insertFront(stuff);
orig._head._next = tmp._root;
orig._head._next = tmp._first;
return result;
}

Expand All @@ -442,20 +471,13 @@ Complexity: $(BIGOH n)
*/
Range linearRemove(Range r)
{
if (!_root)
if (!_first)
{
enforce(!r._head);
return this[];
}
if (_root == r._head)
{
clear();
}
else
{
auto n = findNode(_root, r._head);
n._next = null;
}
auto n = findNode(_root, r._head);
n._next = null;
return Range(null);
}

Expand All @@ -470,7 +492,7 @@ Complexity: $(BIGOH n)
{
auto orig = r.source;
// We have something to remove here
if (orig._head == _root)
if (orig._head == _first)
{
// remove straight from the head of the list
for (; !r.empty; r.popFront())
Expand All @@ -485,7 +507,7 @@ Complexity: $(BIGOH n)
return orig;
}
// Remove from somewhere in the middle of the list
enforce(_root);
enforce(_first);
auto n1 = findNode(_root, orig._head);
auto n2 = findLastNode(orig._head, r.maxLength);
n1._next = n2._next;
Expand All @@ -496,6 +518,22 @@ Complexity: $(BIGOH n)
alias stableLinearRemove = linearRemove;
}

unittest
{
import std.algorithm : 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]));
}

unittest
{
auto s = SList!int(1, 2, 3);
Expand Down

0 comments on commit 647385f

Please sign in to comment.