1 module libd.datastructures.linkedlist; 2 3 import libd.util.errorhandling : onOutOfMemoryError; 4 import libd.memory; 5 import libd.memory.ptr; 6 import libd.algorithm : OptimisationHint; 7 8 // "Default" linked list is a double linked list since that's easier for me to implement >xP 9 @nogc nothrow 10 @(OptimisationHint.rangeFasterThanIndex) 11 struct LinkedList(alias T, alias AllocT = SystemAllocator) 12 { 13 static struct Node 14 { 15 T value; 16 Node* next; 17 Node* prev; 18 } 19 20 private size_t _length; 21 private MaybeNullPtr!(Node, AllocT.Tag) _head; 22 private MaybeNullPtr!(Node, AllocT.Tag) _tail; 23 private AllocatorWrapperOf!AllocT _alloc; 24 25 @disable this(this) {} 26 27 this()(AllocatorWrapperOf!AllocT alloc) 28 { 29 this._alloc = alloc; 30 } 31 32 @nogc nothrow 33 ~this() 34 { 35 while(this._head !is null) 36 { 37 auto head = this._head; 38 this._head = head.next; 39 this._alloc.dispose(head); 40 } 41 } 42 43 alias put = putTail; 44 void putTail()(auto ref T value) 45 { 46 auto node = this._alloc.make!Node(value); 47 if(node is null) 48 onOutOfMemoryError(null); 49 50 if(this._head is null) 51 { 52 this._head = node; 53 this._tail = node; 54 } 55 else 56 { 57 auto tail = this._tail; 58 this._tail.next = node; 59 node.prev = tail; 60 this._tail = node; 61 } 62 63 this._length++; 64 } 65 66 void putTail(Args...)(scope auto ref Args args) 67 { 68 foreach(ref value; args) 69 this.putTail(value); 70 } 71 72 void moveTail()(auto ref T value) 73 { 74 auto node = this._alloc.make!Node(); 75 if(node is null) 76 onOutOfMemoryError(null); 77 move(value, node.value); 78 79 if(this._head is null) 80 { 81 this._head = node; 82 this._tail = node; 83 } 84 else 85 { 86 auto tail = this._tail; 87 this._tail.next = node; 88 node.prev = tail; 89 this._tail = node; 90 } 91 92 this._length++; 93 } 94 95 void putHead()(auto ref T value) 96 { 97 auto node = this._alloc.make!Node(value); 98 if(node is null) 99 onOutOfMemoryError(null); 100 101 if(this._head is null) 102 { 103 this._head = node; 104 this._tail = node; 105 } 106 else 107 { 108 auto head = this._head; 109 this._head.prev = node; 110 node.next = head; 111 this._head = node; 112 } 113 114 this._length++; 115 } 116 117 void insertAt()(size_t index, auto ref T value) 118 { 119 auto node = this._alloc.make!Node(value); 120 if(node is null) 121 onOutOfMemoryError(null); 122 123 assert(index <= this._length, "Index out of bounds."); 124 125 if(this._length == 0) // Special case 126 { 127 this._head = node; 128 this._tail = node; 129 } 130 else if(index == 0) // Special case 131 { 132 this._head.prev = node; 133 node.next = this._head; 134 this._head = node; 135 } 136 else if(index == this._length) // Special case 137 { 138 this._tail.next = node; 139 node.prev = this._tail; 140 this._tail = node; 141 } 142 else 143 { 144 auto head = this._head; 145 foreach(i; 0..index) 146 head = head.next; 147 assert(head !is null); 148 head = head.prev; 149 150 auto next = head.next; 151 head.next = node; 152 node.next = next; 153 node.prev = head; 154 if(next !is null) 155 next.prev = node; 156 } 157 158 this._length++; 159 } 160 161 alias getAt = getAtHead; 162 ref inout(T) getAtHead()(size_t index) inout 163 { 164 return this.getNodeAtHead(index).value; 165 } 166 167 alias removeAt = removeAtHead; 168 void removeAtHead()(size_t index, scope ref T dest) 169 { 170 this.removeAtImpl!getNodeAtHead(index, dest); 171 } 172 173 T removeAtHead()(size_t index) 174 { 175 T value; 176 this.removeAtHead(index, value); 177 return value; 178 } 179 180 void removeAtTail()(size_t index, scope ref T dest) 181 { 182 this.removeAtImpl!getNodeAtTail(index, dest); 183 } 184 185 T removeAtTail()(size_t index) 186 { 187 T value; 188 this.removeAtTail(index, value); 189 return value; 190 } 191 192 @property @trusted 193 auto range() inout 194 { 195 static struct Range 196 { 197 Node* node; 198 199 @nogc nothrow: 200 201 bool empty() { return this.node is null; } 202 void popFront() 203 { 204 assert(!this.empty, "Cannot pop an empty range."); 205 this.node = this.node.next; 206 } 207 ref T front() 208 { 209 assert(!this.empty, "Cannot access front of an empty range."); 210 return this.node.value; 211 } 212 } 213 214 return Range(cast(Node*)this._head.ptr); 215 } 216 217 @property @safe 218 size_t length() const 219 { 220 return this._length; 221 } 222 223 @safe size_t opDollar() const { return this.length; } 224 225 @safe 226 ref inout(T) opIndex(size_t index) inout 227 { 228 return this.getAt(index); 229 } 230 231 private void removeAtImpl(alias GetterFunc)(size_t index, scope ref T dest) 232 { 233 auto node = GetterFunc(index); 234 move(node.value, dest); 235 236 if(this._length == 1) 237 { 238 this._head = null; 239 this._tail = null; 240 } 241 else if(index == 0) 242 { 243 if(node.next !is null) 244 node.next.prev = null; 245 this._head = node.next; 246 } 247 else if(index == this._length-1) 248 { 249 if(node.prev !is null) 250 node.prev.next = null; 251 this._tail = node.prev; 252 } 253 else 254 { 255 node.prev.next = node.next; 256 node.next.prev = node.prev; 257 } 258 259 this._alloc.dispose(node); 260 this._length--; 261 } 262 263 private inout(Node)* getNodeAtHead()(size_t index) inout 264 { 265 assert(index < this._length, "Index out of bounds."); 266 267 auto result = cast()this._head.ptr; 268 foreach(i; 0..index) 269 result = result.next; 270 271 assert(result !is null, "Could not find result?"); 272 return result; 273 } 274 275 private inout(Node)* getNodeAtTail()(size_t index) inout 276 { 277 assert(index < this._length, "Index out of bounds."); 278 279 auto result = cast()this._tail.ptr; 280 const iterations = (this._length - index) - 1; 281 foreach(i; 0..iterations) 282 result = result.prev; 283 284 assert(result !is null, "Could not find result?"); 285 return result; 286 } 287 } 288 /// 289 @("LinkedList - basic") 290 unittest 291 { 292 import libd.algorithm : isCollection, isInputRange; 293 import libd.memory : emplaceInit; 294 static assert(isCollection!(LinkedList!int, int)); 295 static assert(isInputRange!(typeof(LinkedList!int.range()))); 296 297 LinkedList!int list; 298 299 list.put(2); 300 assert(list.length == 1); 301 assert(list._head is list._tail); 302 assert(list.getAt(0) == 2); 303 list.put(4); 304 assert(list.length == 2); 305 assert(list._head !is list._tail); 306 assert(list.getAt(0) == 2); 307 assert(list.getAt(1) == 4); 308 list.putHead(0); 309 assert(list.length == 3); 310 assert(list.getAt(0) == 0); 311 assert(list.getAt(1) == 2); 312 assert(list.getAt(2) == 4); 313 list.getAt(1) /= 2; 314 assert(list.getAt(1) == 1); 315 list.removeAt(1); 316 assert(list.length == 2); 317 assert(list.getAt(0) == 0); 318 assert(list.getAt(1) == 4); 319 list.removeAt(0); 320 assert(list.length == 1); 321 assert(list.getAt(0) == 4); 322 assert(list.removeAt(0)); 323 assert(list.length == 0); 324 assert(list._head is list._tail); 325 assert(list._head is null); 326 list.put(0); 327 list.put(0); 328 list.put(0); 329 330 int next = 2; 331 foreach(ref num; list.range) 332 { 333 num = next; 334 next += 2; 335 } 336 assert(list[0] == 2); 337 assert(list[1] == 4); 338 assert(list[2] == 6); 339 340 emplaceInit(list); 341 list.insertAt(0, 2); // 2 342 list.insertAt(0, 0); // 0 2 343 list.insertAt(2, 3); // 0 2 3 344 list.insertAt(1, 1); // 0 1 2 3 345 assert(list.length == 4); 346 assert(list[0] == 0); 347 assert(list[1] == 1); 348 assert(list[2] == 2); 349 assert(list[3] == 3); 350 assert(list.removeAtTail(1) == 1); 351 assert(list.removeAtTail(1) == 2); 352 assert(list.removeAtTail(0) == 0); 353 assert(list.removeAtTail(0) == 3); 354 }