1 module cachetools.containers.lists; 2 3 private import std.experimental.allocator; 4 private import std.experimental.allocator.mallocator : Mallocator; 5 private import std.experimental.logger; 6 private import std.format; 7 8 /// 9 /// N-way multilist 10 struct MultiDList(T, int N, Allocator = Mallocator) { 11 alias allocator = Allocator.instance; 12 struct Node { 13 T payload; 14 private: 15 Link[N] links; 16 Node* next(size_t i) @safe @nogc { 17 return links[i].next; 18 } 19 20 Node* prev(size_t i) @safe @nogc { 21 return links[i].prev; 22 } 23 24 alias payload this; 25 } 26 27 private { 28 struct Link { 29 Node* prev; 30 Node* next; 31 } 32 33 Node*[N] _heads; 34 Node*[N] _tails; 35 size_t _length; 36 37 } 38 size_t length() const pure nothrow @safe @nogc { 39 return _length; 40 } 41 42 Node* insert_last(T v) @safe nothrow 43 out { 44 assert(_length > 0); 45 } 46 do { 47 auto n = make!(Node)(allocator, v); 48 static foreach (index; 0 .. N) { 49 if (_heads[index] is null) { 50 _heads[index] = n; 51 } 52 n.links[index].prev = _tails[index]; 53 if (_tails[index]!is null) { 54 _tails[index].links[index].next = n; 55 } 56 _tails[index] = n; 57 } 58 _length++; 59 return n; 60 } 61 62 void move_to_tail(Node* n, size_t i) @safe @nogc 63 in { 64 assert(i < N); 65 assert(_length > 0); 66 } 67 out { 68 assert(_heads[i]!is null && _tails[i]!is null); 69 } 70 do { 71 if (n == _tails[i]) { 72 return; 73 } 74 // unlink 75 if (n.links[i].prev is null) { 76 _heads[i] = n.links[i].next; 77 } else { 78 n.links[i].prev.links[i].next = n.links[i].next; 79 } 80 if (n.links[i].next is null) { 81 _tails[i] = n.links[i].prev; 82 } else { 83 n.links[i].next.links[i].prev = n.links[i].prev; 84 } 85 // insert back 86 if (_heads[i] is null) { 87 _heads[i] = n; 88 } 89 n.links[i].prev = _tails[i]; 90 if (_tails[i]!is null) { 91 _tails[i].links[i].next = n; 92 } 93 n.links[i].next = null; 94 _tails[i] = n; 95 } 96 97 void remove(Node* n) nothrow @safe @nogc { 98 if (n is null || _length == 0) { 99 return; 100 } 101 static foreach (i; 0 .. N) { 102 if (n.links[i].prev !is null) { 103 n.links[i].prev.links[i].next = n.links[i].next; 104 } 105 if (n.links[i].next !is null) { 106 n.links[i].next.links[i].prev = n.links[i].prev; 107 } 108 if (n == _tails[i]) { 109 _tails[i] = n.links[i].prev; 110 } 111 if (n == _heads[i]) { 112 _heads[i] = n.links[i].next; 113 } 114 } 115 (() @trusted { dispose(allocator, n); })(); 116 _length--; 117 } 118 119 Node* tail(size_t i) pure nothrow @safe @nogc { 120 return _tails[i]; 121 } 122 123 Node* head(size_t i) pure nothrow @safe @nogc { 124 return _heads[i]; 125 } 126 127 void clear() nothrow @safe @nogc { 128 while (_length > 0) { 129 auto n = _heads[0]; 130 remove(n); 131 } 132 } 133 } 134 135 @safe unittest { 136 import std.algorithm; 137 import std.stdio; 138 import std.range; 139 140 struct Person { 141 string name; 142 int age; 143 } 144 145 MultiDList!(Person*, 2) mdlist; 146 Person[3] persons = [{"Alice", 11}, {"Bob", 9}, {"Carl", 10}]; 147 foreach (i; 0 .. persons.length) { 148 mdlist.insert_last(&persons[i]); 149 } 150 enum NameIndex = 0; 151 enum AgeIndex = 1; 152 assert(mdlist.head(NameIndex).payload.name == "Alice"); 153 assert(mdlist.head(AgeIndex).payload.age == 11); 154 assert(mdlist.tail(NameIndex).payload.name == "Carl"); 155 assert(mdlist.tail(AgeIndex).payload.age == 10); 156 auto alice = mdlist.head(NameIndex); 157 auto bob = alice.next(NameIndex); 158 auto carl = bob.next(NameIndex); 159 mdlist.move_to_tail(alice, AgeIndex); 160 assert(mdlist.tail(AgeIndex).payload.age == 11); 161 mdlist.remove(alice); 162 assert(mdlist.head(NameIndex).payload.name == "Bob"); 163 assert(mdlist.tail(NameIndex).payload.name == "Carl"); 164 assert(mdlist.head(AgeIndex).payload.age == 9); 165 assert(mdlist.tail(AgeIndex).payload.age == 10); 166 mdlist.insert_last(&persons[0]); // B, C, A 167 mdlist.remove(carl); // B, A 168 alice = mdlist.tail(NameIndex); 169 assert(mdlist.length == 2); 170 assert(alice.payload.name == "Alice"); 171 assert(alice.payload.age == 11); 172 assert(mdlist.head(NameIndex).payload.name == "Bob"); 173 assert(mdlist.head(AgeIndex).payload.age == 9); 174 assert(alice.prev(AgeIndex) == bob); 175 assert(alice.prev(NameIndex) == bob); 176 assert(bob.prev(AgeIndex) is null); 177 assert(bob.prev(NameIndex) is null); 178 assert(bob.next(AgeIndex) == alice); 179 assert(bob.next(NameIndex) == alice); 180 mdlist.insert_last(&persons[2]); // B, A, C 181 carl = mdlist.tail(NameIndex); 182 mdlist.move_to_tail(alice, AgeIndex); 183 assert(bob.next(AgeIndex) == carl); 184 assert(bob.next(NameIndex) == alice); 185 } 186 187 struct DList(T, Allocator = Mallocator) { 188 this(this) @disable; 189 struct Node(T) { 190 T payload; 191 private Node!T* prev; 192 private Node!T* next; 193 alias payload this; 194 } 195 196 private { 197 alias allocator = Allocator.instance; 198 Node!T* _head; 199 Node!T* _tail; 200 ulong _length; 201 } 202 203 invariant { 204 assert((_length > 0 && _head !is null && _tail !is null) || (_length == 0 205 && _tail is null && _tail is null) || (_length == 1 && _tail == _head && _head !is null), 206 "length: %s, head: %s, tail: %s".format(_length, _head, _tail)); 207 } 208 209 ulong length() const pure nothrow @safe @nogc { 210 return _length; 211 } 212 213 Node!T* insert_last(T v) @safe nothrow 214 out { 215 assert(_length > 0); 216 assert(_head !is null && _tail !is null); 217 } 218 do { 219 auto n = make!(Node!T)(allocator); 220 n.payload = v; 221 if (_head is null) { 222 _head = n; 223 } 224 n.prev = _tail; 225 if (_tail !is null) { 226 _tail.next = n; 227 } 228 _tail = n; 229 _length++; 230 return n; 231 } 232 233 alias insertFront = insert_first; 234 Node!T* insert_first(T v) @safe nothrow 235 out { 236 assert(_length > 0); 237 assert(_head !is null && _tail !is null); 238 } 239 do { 240 auto n = make!(Node!T)(allocator); 241 n.payload = v; 242 if (_tail is null) { 243 _tail = n; 244 } 245 n.next = _head; 246 if (_head !is null) { 247 _head.prev = n; 248 } 249 _head = n; 250 _length++; 251 return n; 252 } 253 254 bool remove(Node!T* n) @safe @nogc 255 in { 256 assert(_length > 0); 257 } 258 do { 259 if (n.prev) { 260 n.prev.next = n.next; 261 } 262 if (n.next) { 263 n.next.prev = n.prev; 264 } 265 if (n == _tail) { 266 _tail = n.prev; 267 } 268 if (n == _head) { 269 _head = n.next; 270 } 271 (() @trusted { dispose(allocator, n); })(); 272 _length--; 273 return true; 274 } 275 276 void move_to_tail(Node!T* n) @safe @nogc 277 in { 278 assert(_length > 0); 279 assert(_head !is null && _tail !is null); 280 } 281 out { 282 assert(_tail == n && n.next is null); 283 } 284 do { 285 if (n == _tail) { 286 return; 287 } 288 // unlink 289 if (n.prev is null) { 290 _head = n.next; 291 } else { 292 n.prev.next = n.next; 293 } 294 if (n.next is null) { 295 _tail = n.prev; 296 } else { 297 n.next.prev = n.prev; 298 } 299 // insert back 300 if (_head is null) { 301 _head = n; 302 } 303 n.prev = _tail; 304 if (_tail !is null) { 305 _tail.next = n; 306 } 307 n.next = null; 308 _tail = n; 309 310 ////debug(cachetools) tracef("n: %s".format(*n)); 311 //assert(n.next !is null); 312 ////debug tracef("m-t-t: %s, tail: %s", *n, *_tail); 313 //assert(n.next, "non-tail entry have no 'next' pointer?"); 314 //if ( _head == n ) { 315 // assert(n.prev is null); 316 // _head = n.next; 317 //} else { 318 // n.prev.next = n.next; 319 //} 320 //// move this node to end 321 //n.next.prev = n.prev; 322 //n.next = null; 323 //tail.next = n; 324 //_tail = n; 325 } 326 327 Node!T* head() @safe @nogc nothrow { 328 return _head; 329 } 330 331 Node!T* tail() @safe @nogc nothrow { 332 return _tail; 333 } 334 } 335 336 struct SList(T, Allocator = Mallocator) { 337 this(this) @safe { 338 // copy items 339 _Node!T* __newFirst, __newLast; 340 auto f = _first; 341 while (f) { 342 auto v = f.v; 343 auto n = make!(_Node!T)(allocator, v); 344 if (__newLast !is null) { 345 __newLast._next = n; 346 } else { 347 __newFirst = n; 348 } 349 __newLast = n; 350 f = f._next; 351 } 352 _first = __newFirst; 353 _last = __newLast; 354 } 355 356 package { 357 struct _Node(T) { 358 T v; 359 _Node!T* _next; 360 } 361 362 alias allocator = Allocator.instance; 363 364 ulong _length; 365 _Node!T* _first; 366 _Node!T* _last; 367 } 368 369 invariant { 370 assert((_length > 0 && _first !is null && _last !is null) || (_length == 0 371 && _first is null && _last is null)); 372 } 373 374 ~this() { 375 clear(); 376 } 377 378 ulong length() const pure @nogc @safe nothrow { 379 return _length; 380 } 381 382 bool empty() @nogc @safe const { 383 return _length == 0; 384 } 385 386 T front() pure @nogc @safe { 387 return _first.v; 388 } 389 390 T back() pure @nogc @safe { 391 return _last.v; 392 } 393 394 T popFront() @nogc @safe nothrow 395 in { 396 assert(_first !is null); 397 } 398 do { 399 T v = _first.v; 400 auto next = _first._next; 401 (() @trusted { dispose(allocator, _first); })(); 402 _first = next; 403 if (_first is null) { 404 _last = null; 405 } 406 _length--; 407 return v; 408 } 409 410 void clear() @nogc @safe { 411 _Node!T* n = _first; 412 while (n !is null) { 413 auto next = n._next; 414 (() @trusted { dispose(allocator, n); })(); 415 n = next; 416 } 417 } 418 419 private struct Range(T) { 420 private { 421 _Node!T* current; 422 } 423 auto front() pure nothrow @safe @nogc @property { 424 return ¤t.v; 425 } 426 427 void popFront() @safe @nogc nothrow { 428 current = current._next; 429 } 430 431 bool empty() pure const nothrow @safe @nogc @property { 432 return current is null; 433 } 434 } 435 436 alias opSlice = range; 437 auto range() { 438 return Range!T(_first); 439 } 440 441 void insertFront(T v) @safe nothrow 442 out { 443 assert(_first !is null && _last !is null); 444 } 445 do { 446 auto n = make!(_Node!T)(allocator, v); 447 if (_first !is null) { 448 n._next = _first; 449 } 450 _first = n; 451 if (_last is null) { 452 _last = n; 453 } 454 _length++; 455 } 456 457 void insertBack(T v) @safe nothrow 458 out { 459 assert(_first !is null && _last !is null); 460 } 461 do { 462 auto n = make!(_Node!T)(allocator, v); 463 if (_last !is null) { 464 _last._next = n; 465 } else { 466 _first = n; 467 } 468 _last = n; 469 _length++; 470 } 471 472 bool remove_by_predicate(scope bool delegate(T) @safe @nogc nothrow f) @nogc @trusted nothrow { 473 bool removed; 474 _Node!T* current = _first; 475 _Node!T* prev = null; 476 while (current !is null) { 477 auto next = current._next; 478 if (!f(current.v)) { 479 prev = current; 480 current = next; 481 continue; 482 } 483 // do remove 484 _length--; 485 removed = true; 486 dispose(allocator, current); 487 if (prev is null) { 488 _first = next; 489 } else { 490 prev._next = next; 491 } 492 if (next is null) { 493 _last = prev; 494 } 495 } 496 return removed; 497 } 498 } 499 500 @safe @nogc nothrow unittest { 501 SList!int l; 502 assert(l.length() == 0); 503 l.insertFront(0); 504 assert(l.front() == 0); 505 l.popFront(); 506 l.insertBack(1); 507 assert(l.front() == 1); 508 assert(l.length() == 1); 509 l.insertBack(2); 510 assert(l.front() == 1); 511 assert(l.back() == 2); 512 assert(l.length() == 2); 513 //log(l.range()); 514 l.popFront(); 515 assert(l.front() == 2); 516 assert(l.back() == 2); 517 assert(l.length() == 1); 518 l.insertBack(3); 519 l.insertBack(4); 520 //foreach(v; l[]){ 521 // log("v=%d\n", *v); 522 //} 523 //log("---\n"); 524 bool removed; 525 removed = l.remove_by_predicate((n) { return n == 2; }); 526 foreach (v; l[]) { 527 //log("v=%d\n", *v); 528 } 529 assert(removed); 530 assert(l.length() == 2); 531 //log("---\n"); 532 removed = l.remove_by_predicate((n) { return n == 4; }); 533 foreach (v; l[]) { 534 //log("v=%d\n", *v); 535 } 536 assert(removed); 537 assert(l.length() == 1); 538 //log("---\n"); 539 removed = l.remove_by_predicate((n) { return n == 3; }); 540 foreach (v; l[]) { 541 //log("v=%d\n", *v); 542 } 543 assert(removed); 544 assert(l.length() == 0); 545 //log("---\n"); 546 removed = l.remove_by_predicate((n) { return n == 3; }); 547 foreach (v; l[]) { 548 //log("v=%d\n", *v); 549 } 550 assert(!removed); 551 assert(l.length() == 0); 552 auto l1 = SList!int(); 553 foreach (i; 0 .. 100) { 554 l1.insertBack(i); 555 } 556 while (l1.length) { 557 l1.popFront(); 558 } 559 foreach (i; 0 .. 100) { 560 l1.insertFront(i); 561 } 562 while (l1.length) { 563 l1.popFront(); 564 } 565 } 566 567 @safe @nogc nothrow unittest { 568 DList!int dlist; 569 auto n0 = dlist.insertFront(0); 570 assert(dlist.head.payload == 0); 571 dlist.remove(n0); 572 auto n1 = dlist.insert_last(1); 573 assert(dlist.length == 1); 574 dlist.remove(n1); 575 assert(dlist.length == 0); 576 577 n1 = dlist.insert_first(1); 578 assert(dlist.length == 1); 579 dlist.remove(n1); 580 assert(dlist.length == 0); 581 582 n1 = dlist.insert_first(1); 583 auto n2 = dlist.insert_last(2); 584 assert(dlist.length == 2); 585 dlist.move_to_tail(n1); 586 assert(dlist.head.payload == 2); 587 assert(dlist.tail.payload == 1); 588 }