1 /** 2 * Singly-linked list. 3 * Copyright: © 2015 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 private import containers.internal.node : shouldAddGCRange; 11 private import std.experimental.allocator.mallocator : Mallocator; 12 13 /** 14 * Single-linked allocator-backed list. 15 * Params: 16 * T = the element type 17 * Allocator = the allocator to use. Defaults to `Mallocator`. 18 * supportGC = true if the container should support holding references to 19 * GC-allocated memory. 20 */ 21 struct SList(T, Allocator = Mallocator, bool supportGC = shouldAddGCRange!T) 22 { 23 /// Disable copying. 24 this(this) @disable; 25 26 private import std.experimental.allocator.common : stateSize; 27 28 static if (stateSize!Allocator != 0) 29 { 30 /// No default construction if an allocator must be provided. 31 this() @disable; 32 33 /** 34 * Use the given `allocator` for allocations. 35 */ 36 this(Allocator allocator) 37 in 38 { 39 static if (is(typeof(allocator is null))) 40 assert(allocator !is null, "Allocator must not be null"); 41 } 42 do 43 { 44 this.allocator = allocator; 45 } 46 } 47 48 ~this() 49 { 50 Node* current = _front; 51 Node* prev = null; 52 while (current !is null) 53 { 54 prev = current; 55 current = current.next; 56 typeid(Node).destroy(prev); 57 static if (useGC) 58 { 59 import core.memory : GC; 60 GC.removeRange(prev); 61 } 62 allocator.dispose(prev); 63 } 64 _front = null; 65 } 66 67 /** 68 * Complexity: O(1) 69 * Returns: the most recently inserted item 70 */ 71 auto front(this This)() inout @property 72 in 73 { 74 assert (!empty, "Accessing .front of empty SList"); 75 } 76 do 77 { 78 alias ET = ContainerElementType!(This, T); 79 return cast(ET) _front.value; 80 } 81 82 /** 83 * Complexity: O(length) 84 * Returns: the least recently inserted item. 85 */ 86 auto back(this This)() inout @property 87 in 88 { 89 assert (!empty, "Accessing .back of empty SList"); 90 } 91 do 92 { 93 alias ET = ContainerElementType!(This, T); 94 95 auto n = _front; 96 for (; front.next !is null; n = n.next) {} 97 return cast(ET) n.value; 98 } 99 100 /** 101 * Removes the first item in the list. 102 * 103 * Complexity: O(1) 104 * Returns: the first item in the list. 105 */ 106 T moveFront() 107 in 108 { 109 assert (!empty, "Accessing .moveFront of empty SList"); 110 } 111 do 112 { 113 Node* f = _front; 114 _front = f.next; 115 T r = f.value; 116 static if (useGC) 117 { 118 import core.memory : GC; 119 GC.removeRange(f); 120 } 121 allocator.dispose(f); 122 --_length; 123 return r; 124 } 125 126 /** 127 * Removes the first item in the list. 128 * 129 * Complexity: O(1) 130 */ 131 void popFront() 132 { 133 Node* f = _front; 134 _front = f.next; 135 static if (useGC) 136 { 137 import core.memory : GC; 138 GC.removeRange(f); 139 } 140 allocator.dispose(f); 141 --_length; 142 } 143 144 /** 145 * Complexity: O(1) 146 * Returns: true if this list is empty 147 */ 148 bool empty() inout pure nothrow @property @safe @nogc 149 { 150 return _front is null; 151 } 152 153 /** 154 * Complexity: O(1) 155 * Returns: the number of items in the list 156 */ 157 size_t length() inout pure nothrow @property @safe @nogc 158 { 159 return _length; 160 } 161 162 /** 163 * Inserts an item at the front of the list. 164 * 165 * Complexity: O(1) 166 * Params: t = the item to insert into the list 167 */ 168 void insertFront(T t) @trusted 169 { 170 _front = make!Node(allocator, _front, t); 171 static if (useGC) 172 { 173 import core.memory : GC; 174 GC.addRange(_front, Node.sizeof); 175 } 176 _length++; 177 } 178 179 /// ditto 180 alias insert = insertFront; 181 182 /// ditto 183 alias insertAnywhere = insertFront; 184 185 /// ditto 186 alias put = insertFront; 187 188 /** 189 * Supports `list ~= item` syntax 190 * 191 * Complexity: O(1) 192 */ 193 void opOpAssign(string op)(T t) if (op == "~") 194 { 195 put(t); 196 } 197 198 /** 199 * Removes the first instance of value found in the list. 200 * 201 * Complexity: O(length) 202 * Returns: true if a value was removed. 203 */ 204 bool remove(V)(V value) @trusted 205 { 206 Node* prev = null; 207 Node* cur = _front; 208 while (cur !is null) 209 { 210 if (cur.value == value) 211 { 212 if (prev !is null) 213 prev.next = cur.next; 214 if (_front is cur) 215 _front = cur.next; 216 static if (shouldAddGCRange!T) 217 { 218 import core.memory : GC; 219 GC.removeRange(cur); 220 } 221 allocator.dispose(cur); 222 _length--; 223 return true; 224 } 225 prev = cur; 226 cur = cur.next; 227 } 228 return false; 229 } 230 231 /** 232 * Forward range interface 233 */ 234 auto opSlice(this This)() inout 235 { 236 return Range!(This)(_front); 237 } 238 239 /** 240 * Removes all elements from the range 241 * 242 * Complexity: O(length) 243 */ 244 void clear() 245 { 246 Node* prev = null; 247 Node* cur = _front; 248 while (cur !is null) 249 { 250 prev = cur; 251 cur = prev.next; 252 static if (shouldAddGCRange!T) 253 { 254 import core.memory : GC; 255 GC.removeRange(prev); 256 } 257 allocator.dispose(prev); 258 } 259 _front = null; 260 _length = 0; 261 } 262 263 private: 264 265 import std.experimental.allocator : make, dispose; 266 import containers.internal.node : shouldAddGCRange; 267 import containers.internal.element_type : ContainerElementType; 268 import containers.internal.mixins : AllocatorState; 269 270 enum bool useGC = supportGC && shouldAddGCRange!T; 271 272 static struct Range(ThisT) 273 { 274 public: 275 ET front() pure nothrow @property @trusted @nogc 276 { 277 return cast(typeof(return)) current.value; 278 } 279 280 void popFront() pure nothrow @safe @nogc 281 { 282 current = current.next; 283 } 284 285 bool empty() const pure nothrow @property @safe @nogc 286 { 287 return current is null; 288 } 289 290 private: 291 alias ET = ContainerElementType!(ThisT, T); 292 const(Node)* current; 293 } 294 295 static struct Node 296 { 297 Node* next; 298 T value; 299 } 300 301 mixin AllocatorState!Allocator; 302 Node* _front; 303 size_t _length; 304 } 305 306 version(emsi_containers_unittest) unittest 307 { 308 import std.string : format; 309 import std.algorithm : canFind; 310 SList!int intList; 311 foreach (i; 0 .. 100) 312 intList.put(i); 313 assert(intList.length == 100, "%d".format(intList.length)); 314 assert(intList.remove(10)); 315 assert(!intList.remove(10)); 316 assert(intList.length == 99); 317 assert(intList[].canFind(9)); 318 assert(!intList[].canFind(10)); 319 SList!string l; 320 l ~= "abcde"; 321 l ~= "fghij"; 322 assert (l.length == 2); 323 } 324 325 version(emsi_containers_unittest) unittest 326 { 327 static class Foo 328 { 329 string name; 330 } 331 332 SList!Foo hs; 333 auto f = new Foo; 334 hs.put(f); 335 }