1 /** 2 * Singly-linked list. 3 * Copyright: © 2014 Economic Modeling Specialists, Intl. 4 * Authors: Brian Schott 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 8 module containers.slist; 9 10 /** 11 * Single-linked allocator-backed list. 12 * Params: 13 * T = the element type 14 * A = the allocator type 15 */ 16 struct SList(T) 17 { 18 /// Disable copying. 19 this(this) @disable; 20 21 ~this() 22 { 23 Node* current = _front; 24 Node* prev = null; 25 while (current !is null) 26 { 27 prev = current; 28 current = current.next; 29 typeid(Node).destroy(prev); 30 static if (shouldAddGCRange!T) 31 { 32 import core.memory : GC; 33 GC.removeRange(prev); 34 } 35 Mallocator.instance.dispose(prev); 36 } 37 _front = null; 38 } 39 40 /** 41 * Returns: the most recently inserted item 42 */ 43 auto front(this This)() @property 44 in 45 { 46 assert (!empty); 47 } 48 body 49 { 50 alias ET = ContainerElementType!(This, T); 51 return cast(ET) _front.value; 52 } 53 54 /** 55 * Removes and returns the first item in the list. 56 */ 57 T moveFront() 58 in 59 { 60 assert (!empty); 61 } 62 body 63 { 64 Node* f = _front; 65 _front = f.next; 66 T r = f.value; 67 static if (shouldAddGCRange!T) 68 { 69 import core.memory : GC; 70 GC.removeRange(f); 71 } 72 Mallocator.instance.dispose(f); 73 --_length; 74 return r; 75 } 76 77 /** 78 * Removes the first item in the list. 79 */ 80 void popFront() 81 { 82 Node* f = _front; 83 _front = f.next; 84 static if (shouldAddGCRange!T) 85 { 86 import core.memory : GC; 87 GC.removeRange(f); 88 } 89 Mallocator.instance.dispose(f); 90 --_length; 91 } 92 93 /** 94 * Returns: true if this list is empty 95 */ 96 bool empty() inout pure nothrow @property @safe @nogc 97 { 98 return _front is null; 99 } 100 101 /** 102 * Returns: the number of items in the list 103 */ 104 size_t length() inout pure nothrow @property @safe @nogc 105 { 106 return _length; 107 } 108 109 /** 110 * Inserts an item at the front of the list. 111 * Params: t = the item to insert into the list 112 */ 113 void insert(T t) @trusted 114 { 115 _front = Mallocator.instance.make!Node(_front, t); 116 static if (shouldAddGCRange!T) 117 { 118 import core.memory : GC; 119 GC.addRange(_front, Node.sizeof); 120 } 121 _length++; 122 } 123 124 /// ditto 125 alias insertFront = insert; 126 127 /// ditto 128 alias put = insert; 129 130 /// Supports $(B list ~= item) syntax 131 void opOpAssign(string op)(T t) if (op == "~") 132 { 133 put(t); 134 } 135 136 /** 137 * Removes the first instance of value found in the list. 138 * Returns: true if a value was removed. 139 */ 140 bool remove(V)(V value) @trusted 141 { 142 Node* prev = null; 143 Node* cur = _front; 144 while (cur !is null) 145 { 146 if (cur.value == value) 147 { 148 if (prev !is null) 149 prev.next = cur.next; 150 if (_front is cur) 151 _front = cur.next; 152 static if (shouldAddGCRange!T) 153 { 154 import core.memory : GC; 155 GC.removeRange(cur); 156 } 157 Mallocator.instance.dispose(cur); 158 _length--; 159 return true; 160 } 161 prev = cur; 162 cur = cur.next; 163 } 164 return false; 165 } 166 167 /** 168 * Forward range interface 169 */ 170 auto range(this This)() 171 { 172 return Range!(This)(_front); 173 } 174 175 /// ditto 176 alias opSlice = range; 177 178 /** 179 * Removes all elements from the range 180 */ 181 void clear() 182 { 183 Node* prev = null; 184 Node* cur = _front; 185 while (cur !is null) 186 { 187 prev = cur; 188 cur = prev.next; 189 static if (shouldAddGCRange!T) 190 { 191 import core.memory : GC; 192 GC.removeRange(prev); 193 } 194 Mallocator.instance.dispose(prev); 195 } 196 _front = null; 197 _length = 0; 198 } 199 200 private: 201 202 import std.experimental.allocator : make, dispose; 203 import std.experimental.allocator.mallocator : Mallocator; 204 import containers.internal.node : shouldAddGCRange; 205 import containers.internal.element_type : ContainerElementType; 206 207 static struct Range(ThisT) 208 { 209 public: 210 ET front() pure nothrow @property @trusted @nogc 211 { 212 return cast(typeof(return)) current.value; 213 } 214 215 void popFront() pure nothrow @safe @nogc 216 { 217 current = current.next; 218 } 219 220 bool empty() const pure nothrow @property @safe @nogc 221 { 222 return current is null; 223 } 224 225 private: 226 alias ET = ContainerElementType!(ThisT, T); 227 const(Node)* current; 228 } 229 230 static struct Node 231 { 232 Node* next; 233 T value; 234 } 235 236 Node* _front; 237 238 size_t _length; 239 } 240 241 unittest 242 { 243 import std.string : format; 244 import std.algorithm : canFind; 245 SList!int intList; 246 foreach (i; 0 .. 100) 247 intList.put(i); 248 assert (intList.length == 100, "%d".format(intList.length)); 249 assert (intList.remove(10)); 250 assert (!intList.remove(10)); 251 assert (intList.length == 99); 252 assert (intList.range.canFind(9)); 253 assert (!intList.range.canFind(10)); 254 SList!string l; 255 l ~= "abcde"; 256 l ~= "fghij"; 257 assert (l.length == 2); 258 }