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