1 /** 2 * Linked list common data structure and intrusive lists 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.container.listnode; 15 16 import std.range : isInputRange; 17 import ae.utils.meta.reference; 18 19 struct ListCommon 20 { 21 mixin template Data(NODEREF, bool HASPREV, bool HASTAIL) 22 { 23 // non-copyable because head/tail can go out of sync 24 // commented-out until AAs can support non-copyable values 25 //@disable this(this) {} 26 27 NODEREF head; 28 static if (HASTAIL) NODEREF tail; 29 30 invariant() 31 { 32 static if (HASPREV) if (head) assert(!head.prev); 33 static if (HASTAIL) if (tail) assert(!tail.next); 34 } 35 } 36 37 mixin template Impl(NODEREF, bool HASPREV, bool HASTAIL, alias data) 38 { 39 void pushFront(NODEREF node) 40 { 41 static if (HASPREV) node.prev = null; 42 node.next = data.head; 43 static if (HASPREV) 44 if (data.head) 45 data.head.prev = node; 46 data.head = node; 47 static if (HASTAIL) 48 if (!data.tail) 49 data.tail = node; 50 } 51 52 static if (HASTAIL) 53 void pushBack(NODEREF node) 54 { 55 node.next = null; 56 static if (HASPREV) node.prev = data.tail; 57 if (data.tail) 58 data.tail.next = node; 59 data.tail = node; 60 if (data.head is null) 61 data.head = node; 62 } 63 64 static if (HASTAIL) 65 deprecated alias pushBack add; 66 67 NODEREF popFront() 68 { 69 assert(data.head); 70 auto result = data.head; 71 72 auto next = data.head.next; 73 if (next) 74 { 75 static if (HASPREV) next.prev = null; 76 } 77 else 78 { 79 static if (HASTAIL) data.tail = null; 80 } 81 data.head = next; 82 result.next = null; 83 return result; 84 } 85 86 static if (HASTAIL && HASPREV) 87 NODEREF popBack() 88 { 89 assert(data.tail); 90 auto result = data.tail; 91 92 auto prev = data.tail.prev; 93 if (prev) 94 prev.next = null; 95 else 96 data.head = null; 97 data.tail = prev; 98 result.prev = null; 99 return result; 100 } 101 102 static if (HASPREV) 103 void remove(NODEREF node) 104 { 105 if (node.prev) 106 node.prev.next = node.next; 107 else 108 data.head = node.next; 109 if (node.next) 110 node.next.prev = node.prev; 111 else 112 static if (HASTAIL) data.tail = node.prev; 113 node.next = node.prev = null; 114 } 115 116 static struct Iterator(bool FORWARD) 117 { 118 NODEREF cursor; 119 120 @property bool empty() { return !cursor; } 121 @property auto ref front() { return mixin(q{cursor} ~ ITEM_EXPR); } 122 void popFront() 123 { 124 static if (FORWARD) 125 cursor = cursor.next; 126 else 127 cursor = cursor.prev; 128 } 129 } 130 131 alias Iterator!true ForwardIterator; 132 static assert(isInputRange!ForwardIterator); 133 134 static if (HASPREV) 135 alias Iterator!false ReverseIterator; 136 137 @property auto iterator() { return ForwardIterator(data.head); } 138 static if (HASPREV && HASTAIL) 139 @property auto reverseIterator() { return ReverseIterator(data.tail); } 140 141 static if (HASPREV) 142 void remove(I)(I iterator) 143 if (is(I==ForwardIterator) || is(I==ReverseIterator)) 144 { 145 return remove(iterator.cursor); 146 } 147 148 int opApply(int delegate(ref typeof(mixin(q{NODEREF.init} ~ ITEM_EXPR))) dg) 149 { 150 int res = 0; 151 for (auto node = data.head; node; node = node.next) 152 { 153 res = dg(mixin(q{node} ~ ITEM_EXPR)); 154 if (res) 155 break; 156 } 157 return res; 158 } 159 160 @property bool empty() 161 { 162 return data.head is null; 163 } 164 165 @property auto ref front() { return mixin(q{data.head} ~ ITEM_EXPR); } 166 static if (HASTAIL) 167 @property auto ref back () { return mixin(q{data.tail} ~ ITEM_EXPR); } 168 } 169 } 170 171 /// Mixin containing the linked-list fields. 172 /// When using *ListContainer, inject it into your custom type. 173 mixin template ListLink(bool HASPREV) 174 { 175 import ae.utils.meta : RefType; 176 alias RefType!(typeof(this)) NODEREF; 177 NODEREF next; 178 static if (HASPREV) NODEREF prev; 179 } 180 181 mixin template SListLink() { mixin ListLink!false; } 182 mixin template DListLink() { mixin ListLink!true ; } 183 deprecated alias DListLink DListItem; 184 185 struct ListNode(T, bool HASPREV) 186 { 187 mixin ListLink!(HASPREV); 188 T value; 189 deprecated alias value item; 190 } 191 192 /// Container for user-specified list nodes. 193 /// Use together with *ListLink. 194 struct ListContainer(Node, bool HASPREV, bool HASTAIL) 195 { 196 enum ITEM_EXPR = q{}; 197 mixin ListCommon.Data!(RefType!Node, HASPREV, HASTAIL) commonData; 198 mixin ListCommon.Impl!(RefType!Node, HASPREV, HASTAIL, commonData) commonImpl; 199 } 200 201 202 /// *List variations for containers of user linked types. 203 template SListContainer(T) 204 { 205 alias ListContainer!(T, false, false) SListContainer; 206 } 207 208 /// ditto 209 template DESListContainer(T) 210 { 211 alias ListContainer!(T, false, true) DESListContainer; 212 } 213 214 /// ditto 215 template DListContainer(T) 216 { 217 alias ListContainer!(T, true, true) DListContainer; 218 } 219 220 /// ditto 221 template SEDListContainer(T) 222 { 223 alias ListContainer!(T, true, false) SEDListContainer; 224 } 225 226 unittest 227 { 228 class C 229 { 230 mixin DListLink; 231 int x; 232 this(int p) { x = p; } 233 } 234 235 DListContainer!C l; 236 237 auto c1 = new C(1); 238 l.pushBack(c1); 239 auto c2 = new C(2); 240 l.pushBack(c2); 241 auto c3 = new C(3); 242 l.pushBack(c3); 243 244 l.remove(c2); 245 246 int[] a; 247 foreach (c; l) 248 a ~= c.x; 249 assert(a == [1, 3]); 250 251 import std.algorithm; 252 assert(equal(l.iterator.map!(c => c.x)(), [1, 3])); 253 }