Add helper module with intrusive singly and doubly linked lists.
This commit is contained in:
parent
cfb4f83113
commit
536fa09780
192
source/vibe/internal/list.d
Normal file
192
source/vibe/internal/list.d
Normal file
|
@ -0,0 +1,192 @@
|
|||
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);
|
||||
}
|
Loading…
Reference in a new issue