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