193 lines
3.7 KiB
D
193 lines
3.7 KiB
D
|
module vibe.internal.list;
|
||
|
|
||
|
import core.atomic;
|
||
|
|
||
|
struct CircularDList(T)
|
||
|
{
|
||
|
private {
|
||
|
T m_pivot;
|
||
|
}
|
||
|
|
||
|
this(T pivot)
|
||
|
{
|
||
|
assert(pivot.prev is null && pivot.next is null);
|
||
|
pivot.next = pivot.prev = pivot;
|
||
|
m_pivot = pivot;
|
||
|
assert(this.empty);
|
||
|
}
|
||
|
|
||
|
bool empty() const { return m_pivot.next is m_pivot; }
|
||
|
|
||
|
|
||
|
@property T front() { return m_pivot.next is m_pivot ? null : m_pivot.next; }
|
||
|
@property T back() { return m_pivot.prev is m_pivot ? null : m_pivot.prev; }
|
||
|
|
||
|
void remove(T elem)
|
||
|
{
|
||
|
assert(elem !is m_pivot);
|
||
|
elem.prev.next = elem.next;
|
||
|
elem.next.prev = elem.prev;
|
||
|
elem.prev = elem.next = null;
|
||
|
}
|
||
|
|
||
|
void insertBefore(T elem, T pivot)
|
||
|
{
|
||
|
assert(elem.prev is null && elem.next is null);
|
||
|
elem.prev = pivot.prev;
|
||
|
elem.next = pivot;
|
||
|
pivot.prev.next = elem;
|
||
|
pivot.prev = elem;
|
||
|
}
|
||
|
|
||
|
void insertFront(T elem) { insertBefore(elem, m_pivot.next); }
|
||
|
|
||
|
void insertBack(T elem) { insertBefore(elem, m_pivot); }
|
||
|
|
||
|
// NOTE: allowed to remove the current element
|
||
|
int opApply(int delegate(T) @safe nothrow del)
|
||
|
@safe nothrow {
|
||
|
T prev = m_pivot;
|
||
|
debug size_t counter = 0;
|
||
|
while (prev.next !is m_pivot) {
|
||
|
auto el = prev.next;
|
||
|
if (auto ret = del(el))
|
||
|
return ret;
|
||
|
if (prev.next is el)
|
||
|
prev = prev.next;
|
||
|
debug assert (++counter < 1_000_000, "Cycle in list?");
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unittest {
|
||
|
static final class C {
|
||
|
C prev, next;
|
||
|
int i;
|
||
|
this(int i) { this.i = i; }
|
||
|
}
|
||
|
|
||
|
alias L = CircularDList!C;
|
||
|
auto l = L(new C(0));
|
||
|
assert(l.empty);
|
||
|
|
||
|
auto c = new C(1);
|
||
|
l.insertBack(c);
|
||
|
assert(!l.empty);
|
||
|
assert(l.front is c);
|
||
|
assert(l.back is c);
|
||
|
foreach (c; l) assert(c.i == 1);
|
||
|
|
||
|
auto c2 = new C(2);
|
||
|
l.insertFront(c2);
|
||
|
assert(!l.empty);
|
||
|
assert(l.front is c2);
|
||
|
assert(l.back is c);
|
||
|
foreach (c; l) assert(c.i == 1 || c.i == 2);
|
||
|
|
||
|
l.remove(c);
|
||
|
assert(!l.empty);
|
||
|
assert(l.front is c2);
|
||
|
assert(l.back is c2);
|
||
|
foreach (c; l) assert(c.i == 2);
|
||
|
|
||
|
l.remove(c2);
|
||
|
assert(l.empty);
|
||
|
}
|
||
|
|
||
|
|
||
|
struct StackSList(T)
|
||
|
{
|
||
|
@safe nothrow:
|
||
|
|
||
|
import core.atomic : cas;
|
||
|
|
||
|
private T m_first;
|
||
|
|
||
|
@property T first() { return m_first; }
|
||
|
@property T front() { return m_first; }
|
||
|
|
||
|
bool empty() const { return m_first is null; }
|
||
|
|
||
|
void add(T elem)
|
||
|
{
|
||
|
debug iterate((el) { assert(el !is elem, "Double-insertion of list element."); return true; });
|
||
|
elem.next = m_first;
|
||
|
m_first = elem;
|
||
|
}
|
||
|
|
||
|
bool remove(T elem)
|
||
|
{
|
||
|
debug uint counter = 0;
|
||
|
T w = m_first, wp;
|
||
|
while (w !is elem) {
|
||
|
if (!w) return false;
|
||
|
wp = w;
|
||
|
w = w.next;
|
||
|
debug assert(++counter < 1_000_000, "Cycle in linked list?");
|
||
|
}
|
||
|
if (wp) wp.next = w.next;
|
||
|
else m_first = w.next;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void filter(scope bool delegate(T el) @safe nothrow del)
|
||
|
{
|
||
|
debug uint counter = 0;
|
||
|
T w = m_first, pw;
|
||
|
while (w !is null) {
|
||
|
auto wnext = w.next;
|
||
|
if (!del(w)) {
|
||
|
if (pw) pw.next = wnext;
|
||
|
else m_first = wnext;
|
||
|
} else pw = w;
|
||
|
w = wnext;
|
||
|
debug assert(++counter < 1_000_000, "Cycle in linked list?");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void iterate(scope bool delegate(T el) @safe nothrow del)
|
||
|
{
|
||
|
debug uint counter = 0;
|
||
|
T w = m_first;
|
||
|
while (w !is null) {
|
||
|
auto wnext = w.next;
|
||
|
if (!del(w)) break;
|
||
|
w = wnext;
|
||
|
debug assert(++counter < 1_000_000, "Cycle in linked list?");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unittest {
|
||
|
static final class C {
|
||
|
C next;
|
||
|
int i;
|
||
|
this(int i) { this.i = i; }
|
||
|
}
|
||
|
|
||
|
alias L = StackSList!C;
|
||
|
L l;
|
||
|
assert(l.empty);
|
||
|
|
||
|
auto c = new C(1);
|
||
|
l.add(c);
|
||
|
assert(!l.empty);
|
||
|
assert(l.front is c);
|
||
|
l.iterate((el) { assert(el.i == 1); return true; });
|
||
|
|
||
|
auto c2 = new C(2);
|
||
|
l.add(c2);
|
||
|
assert(!l.empty);
|
||
|
assert(l.front is c2);
|
||
|
l.iterate((el) { assert(el.i == 1 || el.i == 2); return true; });
|
||
|
|
||
|
l.remove(c);
|
||
|
assert(!l.empty);
|
||
|
assert(l.front is c2);
|
||
|
l.iterate((el) { assert(el.i == 2); return true; });
|
||
|
|
||
|
l.filter((el) => el.i == 0);
|
||
|
assert(l.empty);
|
||
|
}
|