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