From 536fa0978010b5ee518ade1981f58deed367e678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Thu, 20 Jul 2017 13:34:39 +0200 Subject: [PATCH] Add helper module with intrusive singly and doubly linked lists. --- source/vibe/internal/list.d | 192 ++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 source/vibe/internal/list.d diff --git a/source/vibe/internal/list.d b/source/vibe/internal/list.d new file mode 100644 index 0000000..cfc4135 --- /dev/null +++ b/source/vibe/internal/list.d @@ -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); +}