1 /// 2 module cachetools.containers.lists; 3 4 private import core.memory; 5 6 private import std.experimental.allocator; 7 private import std.experimental.allocator.mallocator : Mallocator; 8 private import std.experimental.allocator.gc_allocator; 9 private import std.experimental.logger; 10 private import std.format; 11 12 private import cachetools.internal; 13 14 /// 15 /// N-way multilist 16 struct MultiDList(T, int N, Allocator = Mallocator, bool GCRangesAllowed = true) 17 { 18 static assert(N>0); 19 alias allocator = Allocator.instance; 20 struct Node { 21 T payload; 22 private: 23 Link[N] links; 24 Node* next(size_t i) @safe @nogc 25 { 26 return links[i].next; 27 } 28 Node* prev(size_t i) @safe @nogc 29 { 30 return links[i].prev; 31 } 32 alias payload this; 33 } 34 private 35 { 36 struct Link 37 { 38 Node* prev; 39 Node* next; 40 } 41 Node*[N] _heads; 42 Node*[N] _tails; 43 size_t _length; 44 45 } 46 ~this() @safe 47 { 48 clear(); 49 } 50 size_t length() const pure nothrow @safe @nogc { 51 return _length; 52 } 53 54 Node* insert_last(T v) 55 out 56 { 57 assert(_length>0); 58 } 59 do 60 { 61 auto n = make!(Node)(allocator, v); 62 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 63 { 64 () @trusted 65 { 66 GC.addRange(n, Node.sizeof); 67 }(); 68 } 69 static foreach(index;0..N) { 70 if ( _heads[index] is null ) { 71 _heads[index] = n; 72 } 73 n.links[index].prev = _tails[index]; 74 if ( _tails[index] !is null ) 75 { 76 _tails[index].links[index].next = n; 77 } 78 _tails[index] = n; 79 } 80 _length++; 81 return n; 82 } 83 84 void move_to_tail(Node* n, size_t i) @safe @nogc 85 in 86 { 87 assert(i < N); 88 assert(_length>0); 89 } 90 out 91 { 92 assert(_heads[i] !is null && _tails[i] !is null); 93 } 94 do 95 { 96 if ( n == _tails[i] ) { 97 return; 98 } 99 // unlink 100 if ( n.links[i].prev is null ) 101 { 102 _heads[i] = n.links[i].next; 103 } 104 else 105 { 106 n.links[i].prev.links[i].next = n.links[i].next; 107 } 108 n.links[i].next.links[i].prev = n.links[i].prev; 109 110 // insert back 111 if ( _heads[i] is null ) { 112 _heads[i] = n; 113 } 114 n.links[i].prev = _tails[i]; 115 if ( _tails[i] !is null ) 116 { 117 _tails[i].links[i].next = n; 118 } 119 n.links[i].next = null; 120 _tails[i] = n; 121 } 122 123 void remove(Node* n) nothrow @safe @nogc 124 { 125 if ( n is null || _length == 0 ) 126 { 127 return; 128 } 129 static foreach(i;0..N) { 130 if ( n.links[i].prev !is null ) { 131 n.links[i].prev.links[i].next = n.links[i].next; 132 } 133 if ( n.links[i].next !is null ) { 134 n.links[i].next.links[i].prev = n.links[i].prev; 135 } 136 if ( n == _tails[i] ) { 137 _tails[i] = n.links[i].prev; 138 } 139 if ( n == _heads[i] ) { 140 _heads[i] = n.links[i].next; 141 } 142 } 143 () @trusted { 144 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 145 { 146 GC.removeRange(n); 147 } 148 dispose(allocator, n); 149 }(); 150 _length--; 151 } 152 Node* tail(size_t i) pure nothrow @safe @nogc 153 { 154 return _tails[i]; 155 } 156 Node* head(size_t i) pure nothrow @safe @nogc 157 { 158 return _heads[i]; 159 } 160 void clear() nothrow @safe @nogc 161 { 162 while(_length>0) 163 { 164 auto n = _heads[0]; 165 remove(n); 166 } 167 } 168 } 169 170 @safe unittest { 171 import std.algorithm; 172 import std.stdio; 173 import std.range; 174 struct Person 175 { 176 string name; 177 int age; 178 } 179 MultiDList!(Person*, 2) mdlist; 180 Person[3] persons = [{"Alice", 11}, {"Bob", 9}, {"Carl", 10}]; 181 foreach(i; 0..persons.length) 182 { 183 mdlist.insert_last(&persons[i]); 184 } 185 enum NameIndex = 0; 186 enum AgeIndex = 1; 187 assert(mdlist.head(NameIndex).payload.name == "Alice"); 188 assert(mdlist.head(AgeIndex).payload.age == 11); 189 assert(mdlist.tail(NameIndex).payload.name == "Carl"); 190 assert(mdlist.tail(AgeIndex).payload.age == 10); 191 auto alice = mdlist.head(NameIndex); 192 auto bob = alice.next(NameIndex); 193 auto carl = bob.next(NameIndex); 194 mdlist.move_to_tail(alice, AgeIndex); 195 assert(mdlist.tail(AgeIndex).payload.age == 11); 196 mdlist.remove(alice); 197 assert(mdlist.head(NameIndex).payload.name == "Bob"); 198 assert(mdlist.tail(NameIndex).payload.name == "Carl"); 199 assert(mdlist.head(AgeIndex).payload.age == 9); 200 assert(mdlist.tail(AgeIndex).payload.age == 10); 201 mdlist.insert_last(&persons[0]); // B, C, A 202 mdlist.remove(carl); // B, A 203 alice = mdlist.tail(NameIndex); 204 assert(mdlist.length == 2); 205 assert(alice.payload.name == "Alice"); 206 assert(alice.payload.age == 11); 207 assert(mdlist.head(NameIndex).payload.name == "Bob"); 208 assert(mdlist.head(AgeIndex).payload.age == 9); 209 assert(alice.prev(AgeIndex) == bob); 210 assert(alice.prev(NameIndex) == bob); 211 assert(bob.prev(AgeIndex) is null); 212 assert(bob.prev(NameIndex) is null); 213 assert(bob.next(AgeIndex) == alice); 214 assert(bob.next(NameIndex) == alice); 215 mdlist.insert_last(&persons[2]); // B, A, C 216 carl = mdlist.tail(NameIndex); 217 mdlist.move_to_tail(alice, AgeIndex); 218 assert(bob.next(AgeIndex) == carl); 219 assert(bob.next(NameIndex) == alice); 220 } 221 222 /// Double linked list 223 struct DList(T, Allocator = Mallocator, bool GCRangesAllowed = true) { 224 this(this) @disable; 225 226 /// 227 struct Node(T) { 228 /// Node content. 229 T payload; 230 private Node!T* prev; 231 private Node!T* next; 232 alias payload this; 233 } 234 private { 235 alias allocator = Allocator.instance; 236 Node!T* _head; 237 Node!T* _tail; 238 ulong _length; 239 240 Node!T* _freelist; 241 uint _freelist_len; 242 enum _freelist_len_max = 100; 243 } 244 245 private struct Range { 246 Node!T* _current; 247 T front() @safe { 248 return _current.payload; 249 } 250 void popFront() @safe { 251 _current = _current.next; 252 } 253 bool empty() @safe { 254 return _current is null; 255 } 256 } 257 258 invariant { 259 assert 260 ( 261 ( _length > 0 && _head !is null && _tail !is null) || 262 ( _length == 0 && _tail is null && _tail is null) || 263 ( _length == 1 && _tail == _head && _head !is null ), 264 "length: %s, head: %s, tail: %s".format(_length, _head, _tail) 265 ); 266 } 267 268 ~this() { 269 clear(); 270 } 271 272 /// Iterator over items 273 Range range() @safe { 274 return Range(_head); 275 } 276 277 /// Number of items in list 278 ulong length() const pure nothrow @safe @nogc { 279 return _length; 280 } 281 282 private void move_to_feelist(Node!T* n) @safe { 283 if ( _freelist_len < _freelist_len_max ) 284 { 285 n.next = _freelist; 286 _freelist = n; 287 ++_freelist_len; 288 } 289 else 290 { 291 () @trusted { 292 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) { 293 GC.removeRange(&n.payload); 294 } 295 dispose(allocator, n); 296 }(); 297 } 298 } 299 300 private Node!T* peek_from_freelist(ref T v) 301 { 302 if ( _freelist_len ) 303 { 304 _freelist_len--; 305 auto r = _freelist; 306 _freelist = r.next; 307 r.next = r.prev = null; 308 r.payload = v; 309 return r; 310 } 311 else 312 { 313 auto n = make!(Node!T)(allocator, v); 314 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) { 315 () @trusted { 316 GC.addRange(&n.payload, T.sizeof); 317 }(); 318 } 319 return n; 320 } 321 } 322 323 /// insert item at list back. 324 alias insertBack = insert_last; 325 /// ditto 326 Node!T* insert_last(T v) 327 out { 328 assert(_length>0); 329 assert(_head !is null && _tail !is null); 330 } 331 do { 332 Node!T* n = peek_from_freelist(v); 333 if ( _head is null ) { 334 _head = n; 335 } 336 n.prev = _tail; 337 if ( _tail !is null ) 338 { 339 _tail.next = n; 340 } 341 _tail = n; 342 _length++; 343 return n; 344 } 345 346 /// insert item at list front 347 alias insertFront = insert_first; 348 /// ditto 349 Node!T* insert_first(T v) 350 out { 351 assert(_length>0); 352 assert(_head !is null && _tail !is null); 353 } 354 do { 355 Node!T* n = peek_from_freelist(v); 356 if ( _tail is null ) { 357 _tail = n; 358 } 359 n.next = _head; 360 if ( _head !is null ) 361 { 362 _head.prev = n; 363 } 364 _head = n; 365 _length++; 366 return n; 367 } 368 369 /// remove all items from list 370 void clear() @safe { 371 Node!T* n = _head, next; 372 while(n) 373 { 374 next = n.next; 375 () @trusted { 376 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) { 377 GC.removeRange(&n.payload); 378 } 379 dispose(allocator, n); 380 }(); 381 n = next; 382 } 383 n = _freelist; 384 while(n) 385 { 386 next = n.next; 387 () @trusted { 388 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) { 389 GC.removeRange(&n.payload); 390 } 391 dispose(allocator, n); 392 }(); 393 n = next; 394 } 395 _length = 0; 396 _freelist_len = 0; 397 _head = _tail = _freelist = null; 398 } 399 400 /** 401 pop front item. 402 Returns: true if list was not empty 403 **/ 404 bool popFront() @safe { 405 if ( _length == 0 ) 406 { 407 return false; 408 } 409 return remove(_head); 410 } 411 412 /** 413 pop last item. 414 Returns: true if list was not empty 415 **/ 416 bool popBack() @safe { 417 if ( _length == 0 ) 418 { 419 return false; 420 } 421 return remove(_tail); 422 } 423 424 /// remove node by pointer. (safe until pointer is correct) 425 bool remove(Node!T* n) @safe @nogc 426 in {assert(_length>0);} 427 do { 428 if ( n.prev ) { 429 n.prev.next = n.next; 430 } 431 if ( n.next ) { 432 n.next.prev = n.prev; 433 } 434 if ( n == _tail ) { 435 _tail = n.prev; 436 } 437 if ( n == _head ) { 438 _head = n.next; 439 } 440 _length--; 441 move_to_feelist(n); 442 return true; 443 } 444 445 /// move node to tail 446 void move_to_tail(Node!T* n) @safe @nogc 447 in { 448 assert(_length > 0); 449 assert(_head !is null && _tail !is null); 450 } 451 out { 452 assert(_tail == n && n.next is null); 453 } 454 do { 455 if ( n == _tail ) { 456 return; 457 } 458 // unlink 459 if ( n.prev is null ) 460 { 461 _head = n.next; 462 } 463 else 464 { 465 n.prev.next = n.next; 466 } 467 n.next.prev = n.prev; 468 // insert back 469 n.prev = _tail; 470 if ( _tail !is null ) 471 { 472 _tail.next = n; 473 } 474 n.next = null; 475 _tail = n; 476 477 } 478 479 /// move to head 480 void move_to_head(Node!T* n) @safe @nogc 481 in { 482 assert(_length > 0); 483 assert(_head !is null && _tail !is null); 484 } 485 out { 486 assert(_head == n && n.prev is null); 487 } 488 do { 489 if ( n == _head ) { 490 return; 491 } 492 // unlink 493 n.prev.next = n.next; 494 if ( n.next is null ) 495 { 496 _tail = n.prev; 497 } 498 else 499 { 500 n.next.prev = n.prev; 501 } 502 503 // insert front 504 n.next = _head; 505 if ( _head !is null ) 506 { 507 _head.prev = n; 508 } 509 n.prev = null; 510 _head = n; 511 512 } 513 514 alias front = head; 515 /** 516 head node 517 Returns: pointer to head node 518 **/ 519 Node!T* head() @safe @nogc nothrow { 520 return _head; 521 } 522 523 alias back = tail; 524 /** Tail node 525 Returns: pointer to tail node. 526 */ 527 Node!T* tail() @safe @nogc nothrow { 528 return _tail; 529 } 530 } 531 532 /// 533 struct SList(T, Allocator = Mallocator, bool GCRangesAllowed = true) { 534 this(this) 535 { 536 // copy items 537 _Node!T* __newFirst, __newLast; 538 auto f = _first; 539 while(f) 540 { 541 auto v = f.v; 542 auto n = make!(_Node!T)(allocator, v); 543 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 544 { 545 () @trusted { 546 GC.addRange(&n.v, T.sizeof); 547 }(); 548 } 549 if ( __newLast !is null ) { 550 __newLast._next = n; 551 } else { 552 __newFirst = n; 553 } 554 __newLast = n; 555 f = f._next; 556 } 557 _first = __newFirst; 558 _last = __newLast; 559 _freelist = null; 560 _freelist_len = 0; 561 } 562 563 void opAssign(typeof(this) other) 564 { 565 // copy items 566 debug(cachetools) safe_tracef("opAssign SList"); 567 _Node!T* __newFirst, __newLast; 568 auto f = other._first; 569 while(f) 570 { 571 auto v = f.v; 572 auto n = make!(_Node!T)(allocator, v); 573 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 574 { 575 () @trusted { 576 GC.addRange(&n.v, T.sizeof); 577 }(); 578 } 579 if ( __newLast !is null ) { 580 __newLast._next = n; 581 } else { 582 __newFirst = n; 583 } 584 __newLast = n; 585 f = f._next; 586 } 587 _first = __newFirst; 588 _last = __newLast; 589 _length = other._length; 590 _freelist = null; 591 _freelist_len = 0; 592 } 593 594 ~this() @safe { 595 clear(); 596 } 597 598 package { 599 struct _Node(T) { 600 T v; 601 _Node!T *_next; 602 } 603 alias allocator = Allocator.instance; 604 605 ulong _length; 606 _Node!T *_first; 607 _Node!T *_last; 608 609 _Node!T* _freelist; 610 uint _freelist_len; 611 enum _freelist_len_max = 100; 612 } 613 614 invariant { 615 try 616 { 617 assert ( 618 ( _length > 0 && _first !is null && _last !is null) || 619 ( _length == 0 && _first is null && _last is null), 620 "length: %d, first: %s, last: %s".format(_length, _first, _last) 621 ); 622 } 623 catch(Exception e) 624 { 625 } 626 } 627 628 /// number of items in list 629 ulong length() const pure @nogc @safe nothrow { 630 return _length; 631 } 632 /// item empty? 633 bool empty() @nogc @safe const { 634 return _length == 0; 635 } 636 /// front item 637 T front() pure @nogc @safe { 638 return _first.v; 639 } 640 /// back item 641 T back() pure @nogc @safe { 642 return _last.v; 643 } 644 645 private void move_to_feelist(_Node!T* n) @safe 646 { 647 if ( _freelist_len < _freelist_len_max ) 648 { 649 n._next = _freelist; 650 _freelist = n; 651 ++_freelist_len; 652 } 653 else 654 { 655 (() @trusted { 656 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 657 { 658 GC.removeRange(&n.v); 659 } 660 dispose(allocator, n); 661 })(); 662 } 663 } 664 private _Node!T* peek_from_freelist() @safe 665 { 666 if ( _freelist_len ) 667 { 668 _freelist_len--; 669 auto r = _freelist; 670 _freelist = r._next; 671 r._next = null; 672 return r; 673 } 674 else 675 { 676 auto n = make!(_Node!T)(allocator); 677 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 678 { 679 () @trusted 680 { 681 GC.addRange(&n.v, T.sizeof); 682 }(); 683 } 684 return n; 685 } 686 } 687 /// pop front item 688 T popFront() @nogc @safe nothrow 689 in { assert(_first !is null); } 690 do { 691 T v = _first.v; 692 auto next = _first._next; 693 _length--; 694 move_to_feelist(_first); 695 _first = next; 696 if ( _first is null ) { 697 _last = null; 698 } 699 return v; 700 } 701 /// clear everything 702 void clear() @nogc @safe { 703 _Node!T* n = _first; 704 while( n !is null ) { 705 auto next = n._next; 706 (() @trusted { 707 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 708 { 709 GC.removeRange(&n.v); 710 } 711 dispose(allocator, n); 712 })(); 713 n = next; 714 } 715 n = _freelist; 716 while( n !is null ) { 717 auto next = n._next; 718 (() @trusted { 719 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) 720 { 721 GC.removeRange(&n.v); 722 } 723 dispose(allocator, n); 724 })(); 725 n = next; 726 } 727 _length = _freelist_len = 0; 728 _first = _last = _freelist = null; 729 } 730 private struct Range(T) { 731 private { 732 _Node!T *current; 733 } 734 auto front() pure nothrow @safe @nogc @property { 735 return ¤t.v; 736 } 737 void popFront() @safe @nogc nothrow { 738 current = current._next; 739 } 740 bool empty() pure const nothrow @safe @nogc @property { 741 return current is null; 742 } 743 } 744 alias opSlice = range; 745 /// return range over list 746 Range!T range() { 747 return Range!T(_first); 748 } 749 /// insert item at front 750 void insertFront(T v) 751 out{ assert(_first !is null && _last !is null);} 752 do { 753 _Node!T* n = peek_from_freelist(); 754 n.v = v; 755 if ( _first !is null ) { 756 n._next = _first; 757 } 758 _first = n; 759 if ( _last is null ) { 760 _last = n; 761 } 762 _length++; 763 } 764 /// insert item at back 765 void insertBack(T v) 766 out{ assert(_first !is null && _last !is null);} 767 do { 768 _Node!T* n = peek_from_freelist(); 769 n.v = v; 770 if ( _last !is null ) { 771 _last._next = n; 772 } else { 773 _first = n; 774 } 775 _last = n; 776 _length++; 777 } 778 /// remove items by predicate 779 bool remove_by_predicate(scope bool delegate(T) @safe @nogc nothrow f) @nogc @trusted nothrow { 780 bool removed; 781 _Node!T *current = _first; 782 _Node!T *prev = null; 783 while (current !is null) { 784 auto next = current._next; 785 if ( !f(current.v) ) { 786 prev = current; 787 current = next; 788 continue; 789 } 790 // do remove 791 _length--; 792 removed = true; 793 static if ( !is(Allocator == GCAllocator) && UseGCRanges!T ) 794 { 795 GC.removeRange(current); 796 } 797 dispose(allocator, current); 798 if ( prev is null ) { 799 _first = next; 800 } else { 801 prev._next = next; 802 } 803 if ( next is null ) { 804 _last = prev; 805 } 806 } 807 return removed; 808 } 809 } 810 811 @safe @nogc nothrow unittest { 812 SList!int l; 813 assert(l.length() == 0); 814 l.insertFront(0); 815 assert(l.front() == 0); 816 l.popFront(); 817 l.insertBack(1); 818 assert(l.front() == 1); 819 assert(l.length() == 1); 820 l.insertBack(2); 821 assert(l.front() == 1); 822 assert(l.back() == 2); 823 assert(l.length() == 2); 824 //log(l.range()); 825 l.popFront(); 826 assert(l.front() == 2); 827 assert(l.back() == 2); 828 assert(l.length() == 1); 829 l.insertBack(3); 830 l.insertBack(4); 831 //foreach(v; l[]){ 832 // log("v=%d\n", *v); 833 //} 834 //log("---\n"); 835 bool removed; 836 removed = l.remove_by_predicate((n){return n==2;}); 837 foreach(v; l[]){ 838 //log("v=%d\n", *v); 839 } 840 assert(removed); 841 assert(l.length()==2); 842 //log("---\n"); 843 removed = l.remove_by_predicate((n){return n==4;}); 844 foreach(v; l[]){ 845 //log("v=%d\n", *v); 846 } 847 assert(removed); 848 assert(l.length()==1); 849 //log("---\n"); 850 removed = l.remove_by_predicate((n){return n==3;}); 851 foreach(v; l[]){ 852 //log("v=%d\n", *v); 853 } 854 assert(removed); 855 assert(l.length()==0); 856 //log("---\n"); 857 removed = l.remove_by_predicate((n){return n==3;}); 858 foreach(v; l[]){ 859 //log("v=%d\n", *v); 860 } 861 assert(!removed); 862 assert(l.length()==0); 863 auto l1 = SList!int(); 864 foreach(i;0..10000) { 865 l1.insertBack(i); 866 } 867 while(l1.length) { 868 l1.popFront(); 869 } 870 foreach(i;0..10000) { 871 l1.insertFront(i); 872 } 873 while(l1.length) { 874 l1.popFront(); 875 } 876 } 877 @safe unittest 878 { 879 class C 880 { 881 int c; 882 this(int v) { 883 c = v; 884 } 885 } 886 SList!C l2; 887 foreach(i;0..10000) { 888 l2.insertBack(new C(i)); 889 } 890 while(l2.length) { 891 l2.popFront(); 892 } 893 } 894 895 @safe nothrow unittest { 896 import std.algorithm.comparison; 897 898 DList!int dlist; 899 () @nogc 900 { 901 auto n0 = dlist.insertFront(0); 902 assert(dlist.head.payload == 0); 903 dlist.remove(n0); 904 auto n1 = dlist.insert_last(1); 905 assert(dlist.length == 1); 906 dlist.remove(n1); 907 assert(dlist.length == 0); 908 909 n1 = dlist.insert_first(1); 910 assert(dlist.length == 1); 911 dlist.remove(n1); 912 assert(dlist.length == 0); 913 914 n1 = dlist.insert_first(1); 915 auto n2 = dlist.insert_last(2); 916 assert(dlist.length == 2); 917 dlist.move_to_tail(n1); 918 assert(dlist.head.payload == 2); 919 assert(dlist.tail.payload == 1); 920 dlist.move_to_head(n1); 921 assert(dlist.head.payload == 1); 922 assert(dlist.tail.payload == 2); 923 dlist.clear(); 924 auto p = dlist.insertBack(1); 925 dlist.insertBack(2); 926 dlist.insertBack(3); 927 dlist.insertFront(0); 928 dlist.move_to_tail(p); 929 dlist.move_to_tail(p); 930 dlist.move_to_head(p); 931 dlist.move_to_head(p); 932 dlist.remove(p); 933 }(); 934 assert(equal(dlist.range(), [0, 2, 3])); 935 dlist.clear(); 936 class C 937 { 938 int c; 939 this(int v) 940 { 941 c = v; 942 } 943 } 944 DList!C dlist_c; 945 // test freelist ops 946 foreach(i;0..1000) 947 { 948 dlist_c.insertBack(new C(i)); 949 } 950 foreach(i;0..500) 951 { 952 dlist_c.popFront(); 953 } 954 assert(dlist_c.length() == 500); 955 dlist_c.clear(); 956 dlist_c.popFront(); 957 dlist_c.popBack(); 958 assert(dlist_c.length() == 0); 959 } 960 961 private byte useFreePosition(ubyte[] m) @safe @nogc nothrow 962 { 963 import core.bitop: bsf; 964 // 965 // find free position, mark it as used and return it 966 // least significant bit in freeMap[0] means _nodes[0] 967 // most significant bit in freeMap[$-1] means nodes[$-1] 968 // 969 auto l = m.length; 970 for(uint i=0; i < l;i++) 971 { 972 ubyte v = m[i]; 973 if ( v < 255 ) 974 { 975 auto p = bsf(v ^ 0xff); 976 m[i] += 1 << p; 977 return cast(byte)((i<<3)+p); 978 } 979 } 980 assert(0); 981 } 982 private void markFreePosition(ubyte[] m, size_t position) @safe @nogc nothrow 983 { 984 auto p = position >> 3; 985 auto b = position & 0x7; 986 m[p] &= (1<<b)^0xff; 987 } 988 989 private bool isFreePosition(ubyte[] m, size_t position) @safe @nogc nothrow 990 { 991 auto p = position >> 3; 992 auto b = position & 0x7; 993 return (m[p] & (1<<b)) == 0; 994 } 995 private ubyte countBusy(ubyte[] m) @safe @nogc nothrow 996 { 997 import core.bitop; 998 ubyte s = 0; 999 foreach(b; m) 1000 { 1001 s+=popcnt(b); 1002 } 1003 return s; 1004 } 1005 @safe unittest 1006 { 1007 import std.experimental.logger; 1008 globalLogLevel = LogLevel.info; 1009 import std.algorithm.comparison: equal; 1010 ubyte[] map = [0,0]; 1011 auto p = useFreePosition(map); 1012 assert(p == 0, "expected 0, got %s".format(p)); 1013 assert(map[0] == 1); 1014 assert(!isFreePosition(map, 0)); 1015 assert(isFreePosition(map, 1)); 1016 1017 p = useFreePosition(map); 1018 assert(p == 1, "expected 1, got %s".format(p)); 1019 map = [255,0]; 1020 p = useFreePosition(map); 1021 assert(p == 8, "expected 8, got %s".format(p)); 1022 assert(map[1] == 1); 1023 map = [255,0x01]; 1024 p = useFreePosition(map); 1025 assert(p == 9, "expected 9, got %s".format(p)); 1026 assert(equal(map, [0xff, 0x03])); 1027 markFreePosition(map, 8); 1028 assert(equal(map, [0xff, 0x02]), "got %s".format(map)); 1029 markFreePosition(map, 9); 1030 assert(equal(map, [0xff, 0x00]), "got %s".format(map)); 1031 markFreePosition(map, 0); 1032 assert(equal(map, [0xfe, 0x00]), "got %s".format(map)); 1033 } 1034 1035 /// 1036 /// Unrolled list 1037 /// 1038 struct CompressedList(T, Allocator = Mallocator, bool GCRangesAllowed = true) 1039 { 1040 alias allocator = Allocator.instance; 1041 alias StoredT = StoredType!T; 1042 //enum MAGIC = 0x00160162; 1043 enum PageSize = 512; // in bytes 1044 enum NodesPerPage = PageSize/Node.sizeof; 1045 static assert(NodesPerPage >= 1, "Node is too large to use this List, use DList instead"); 1046 static assert(NodesPerPage <= 255, "Strange, but Node size is too small to use this List, use DList instead"); 1047 1048 enum BitMapLength = NodesPerPage % 8 ? NodesPerPage/8+1 : NodesPerPage/8; 1049 1050 /// 1051 /// unrolled list with support only for: 1052 /// 1. insert/delete front 1053 /// 2. insert/delete back 1054 /// 3. keep unstable "pointer" to arbitrary element 1055 /// 4. remove element by pointer 1056 1057 struct Page { 1058 /// 1059 /// Page is fixed-length array of list Nodes 1060 /// with batteries 1061 /// 1062 //uint _magic = MAGIC; 1063 //uint _epoque; // increment each time we move to freelist 1064 ubyte[BitMapLength] _freeMap; 1065 Page* _prevPage; 1066 Page* _nextPage; 1067 byte _firstNode; 1068 byte _lastNode; 1069 ubyte _count; // nodes counter 1070 Node[NodesPerPage] _nodes; 1071 } 1072 1073 struct Node { 1074 StoredT v; 1075 byte p; // prev index 1076 byte n; // next index 1077 } 1078 1079 struct NodePointer { 1080 private 1081 { 1082 Page* _page; 1083 byte _index; 1084 } 1085 this(Page* page, byte index) 1086 { 1087 //_epoque = page._epoque; 1088 _page = page; 1089 _index = index; 1090 } 1091 /// 1092 /// This is unsafe as you may refer to deleted node. 1093 /// You are free to wrap it in @trusted code if you know what are you doing. 1094 /// 1095 T opUnary(string s)() @system if (s == "*") 1096 { 1097 assert(_page !is null); 1098 //assert(_page._magic == MAGIC, "Pointer resolution to freed or damaged page"); 1099 //assert(_page._epoque == _epoque, "Page were freed"); 1100 assert(!isFreePosition(_page._freeMap, _index), "you tried to access already free list element"); 1101 return _page._nodes[_index].v; 1102 } 1103 } 1104 1105 struct Range { 1106 private Page* page; 1107 private byte index; 1108 1109 T front() @safe { 1110 if ( page !is null && index == -1) 1111 { 1112 index = page._firstNode; 1113 } 1114 return page._nodes[index].v; 1115 } 1116 void popFront() @safe { 1117 if ( page !is null && index == -1) 1118 { 1119 index = page._firstNode; 1120 } 1121 index = page._nodes[index].n; 1122 if ( index != -1 ) 1123 { 1124 return; 1125 } 1126 page = page._nextPage; 1127 if ( page is null ) 1128 { 1129 return; 1130 } 1131 index = page._firstNode; 1132 } 1133 bool empty() const @safe { 1134 return page is null; 1135 } 1136 } 1137 /// Iterator over items. 1138 Range range() @safe { 1139 return Range(_pages_first, -1); 1140 } 1141 private 1142 { 1143 Page* _pages_first, _pages_last; 1144 ulong _length; 1145 1146 Page* _freelist; 1147 int _freelist_len; 1148 enum _freelist_len_max = 100; 1149 } 1150 this(this) { 1151 auto r = range(); 1152 _pages_first = _pages_last = _freelist = null; 1153 _length = 0; 1154 _freelist_len = 0; 1155 foreach(e; r) { 1156 insertBack(e); 1157 } 1158 } 1159 private void move_to_freelist(Page* page) @safe @nogc { 1160 if ( _freelist_len >= _freelist_len_max ) 1161 { 1162 debug(cachetools) safe_tracef("dispose page"); 1163 () @trusted { 1164 static if ( UseGCRanges!(Allocator,T, GCRangesAllowed) ) { 1165 GC.removeRange(&page._nodes[0]); 1166 } 1167 dispose(allocator, page); 1168 }(); 1169 return; 1170 } 1171 debug(cachetools) safe_tracef("put page in freelist"); 1172 //page._epoque++; 1173 page._nextPage = _freelist; 1174 _freelist = page; 1175 _freelist_len++; 1176 } 1177 1178 private Page* peek_from_freelist() @safe { 1179 if ( _freelist is null ) 1180 { 1181 Page* page = make!Page(allocator); 1182 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) { 1183 () @trusted { 1184 GC.addRange(&page._nodes[0], Node.sizeof * NodesPerPage); 1185 }(); 1186 } 1187 _freelist = page; 1188 _freelist_len = 1; 1189 } 1190 Page* p = _freelist; 1191 _freelist = p._nextPage; 1192 _freelist_len--; 1193 assert(_freelist_len>=0 && _freelist_len < _freelist_len_max); 1194 p._nextPage = p._prevPage = null; 1195 p._firstNode = p._lastNode = -1; 1196 return p; 1197 } 1198 1199 ~this() @safe { 1200 clear(); 1201 } 1202 1203 /// remove anything from list 1204 void clear() @safe { 1205 _length = 0; 1206 Page* page = _pages_first, next; 1207 while(page) 1208 { 1209 next = page._nextPage; 1210 () @trusted { 1211 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) { 1212 GC.removeRange(page); 1213 } 1214 dispose(allocator, page); 1215 }(); 1216 page = next; 1217 } 1218 page = _freelist; 1219 while(page) 1220 { 1221 next = page._nextPage; 1222 () @trusted { 1223 static if ( UseGCRanges!(Allocator, T, GCRangesAllowed) ) { 1224 GC.removeRange(page); 1225 } 1226 dispose(allocator, page); 1227 }(); 1228 page = next; 1229 } 1230 _length = _freelist_len = 0; 1231 _pages_first = _pages_last = _freelist = null; 1232 } 1233 1234 /// Is list empty? 1235 bool empty() @safe const { 1236 return _length == 0; 1237 } 1238 1239 /// Items in the list. 1240 ulong length() @safe const { 1241 return _length; 1242 } 1243 1244 /// remove node (by 'Pointer') 1245 void remove(ref NodePointer p) @system { 1246 if ( empty ) 1247 { 1248 assert(0, "Tried to remove from empty list"); 1249 } 1250 _length--; 1251 Page *page = p._page; 1252 byte index = p._index; 1253 assert(!isFreePosition(page._freeMap, index), "you tried to remove already free list element"); 1254 with (page) 1255 { 1256 assert(_count>0); 1257 _count--; 1258 // unlink from list 1259 auto next = _nodes[index].n; 1260 auto prev = _nodes[index].p; 1261 if ( prev >= 0) 1262 { 1263 _nodes[prev].n = next; 1264 } 1265 else 1266 { 1267 _firstNode = next; 1268 } 1269 if ( next >= 0) 1270 { 1271 _nodes[next].p = prev; 1272 } 1273 else 1274 { 1275 _lastNode = prev; 1276 } 1277 //_nodes[index].n = _nodes[index].p = -1; 1278 markFreePosition(_freeMap, index); 1279 } 1280 if ( page._count == 0 ) 1281 { 1282 // relase this page 1283 if ( _pages_first == page ) 1284 { 1285 assert(page._prevPage is null); 1286 _pages_first = page._nextPage; 1287 } 1288 if ( _pages_last == page ) 1289 { 1290 assert(page._nextPage is null); 1291 _pages_last = page._prevPage; 1292 } 1293 if ( page._nextPage !is null ) 1294 { 1295 page._nextPage._prevPage = page._prevPage; 1296 } 1297 if ( page._prevPage !is null ) 1298 { 1299 page._prevPage._nextPage = page._nextPage; 1300 } 1301 move_to_freelist(page); 1302 } 1303 // at this point page can be disposed 1304 } 1305 1306 /// List front item 1307 T front() @safe { 1308 if ( empty ) 1309 { 1310 assert(0, "Tried to access front of empty list"); 1311 } 1312 Page* p = _pages_first; 1313 assert( p !is null); 1314 assert( p._count > 0 ); 1315 with(p) 1316 { 1317 return _nodes[_firstNode].v; 1318 } 1319 } 1320 1321 /// Pop front item 1322 void popFront() @safe { 1323 if ( empty ) 1324 { 1325 assert(0, "Tried to popFront from empty list"); 1326 } 1327 _length--; 1328 Page* page = _pages_first; 1329 debug(cachetools) safe_tracef("popfront: page before: %s", *page); 1330 assert(page !is null); 1331 with (page) { 1332 assert(_count>0); 1333 assert(!isFreePosition(_freeMap, _firstNode)); 1334 auto first = _firstNode; 1335 auto next = _nodes[first].n; 1336 markFreePosition(_freeMap, first); 1337 if ( next >= 0 ) 1338 { 1339 _nodes[next].p = -1; 1340 } 1341 //_nodes[first].n = _nodes[first].p = -1; 1342 _count--; 1343 _firstNode = next; 1344 } 1345 if ( page._count == 0 ) 1346 { 1347 // relase this page 1348 _pages_first = page._nextPage; 1349 move_to_freelist(page); 1350 if ( _pages_first is null ) 1351 { 1352 _pages_last = null; 1353 } 1354 else 1355 { 1356 _pages_first._prevPage = null; 1357 } 1358 } 1359 debug(cachetools) safe_tracef("popfront: page after: %s", *page); 1360 } 1361 1362 /// Insert item at front. 1363 NodePointer insertFront(T v) { 1364 _length++; 1365 Page* page = _pages_first; 1366 if ( page is null ) 1367 { 1368 page = peek_from_freelist(); 1369 _pages_first = _pages_last = page; 1370 } 1371 if (page._count == NodesPerPage) 1372 { 1373 Page* new_page = peek_from_freelist(); 1374 new_page._nextPage = page; 1375 page._prevPage = new_page; 1376 _pages_first = new_page; 1377 page = new_page; 1378 } 1379 // there is free space 1380 auto index = useFreePosition(page._freeMap); 1381 assert(index < NodesPerPage); 1382 page._nodes[index].v = v; 1383 page._nodes[index].p = -1; 1384 page._nodes[index].n = page._firstNode; 1385 if (page._count == 0) 1386 { 1387 page._firstNode = page._lastNode = cast(ubyte)index; 1388 } 1389 else 1390 { 1391 assert(page._firstNode >= 0); 1392 assert(!isFreePosition(page._freeMap, page._firstNode)); 1393 page._nodes[page._firstNode].p = cast(ubyte)index; 1394 page._firstNode = cast(ubyte)index; 1395 } 1396 page._count++; 1397 assert(page._count == countBusy(page._freeMap)); 1398 debug(cachetools) safe_tracef("page: %s", *page); 1399 return NodePointer(page, index); 1400 } 1401 1402 /// List back item. 1403 T back() @safe { 1404 if ( empty ) 1405 { 1406 assert(0, "Tried to access back of empty list"); 1407 } 1408 Page* p = _pages_last; 1409 assert( p !is null); 1410 assert( p._count > 0 ); 1411 debug(cachetools) safe_tracef("page: %s", *p); 1412 with(p) 1413 { 1414 return _nodes[_lastNode].v; 1415 } 1416 } 1417 1418 /// Pop back item from list. 1419 void popBack() @safe { 1420 if ( empty ) 1421 { 1422 assert(0, "Tried to popBack from empty list"); 1423 } 1424 _length--; 1425 Page* page = _pages_last; 1426 assert(page !is null); 1427 with (page) { 1428 assert(_count>0); 1429 assert(!isFreePosition(_freeMap, _lastNode)); 1430 auto last = _lastNode; 1431 auto prev = _nodes[last].p; 1432 markFreePosition(_freeMap, last); 1433 if ( prev >=0 ) 1434 { 1435 _nodes[prev].n = -1; 1436 } 1437 //_nodes[last].n = _nodes[last].p = -1; 1438 _count--; 1439 _lastNode = prev; 1440 } 1441 if ( page._count == 0 ) 1442 { 1443 debug(cachetools) safe_tracef("release page"); 1444 // relase this page 1445 _pages_last = page._prevPage; 1446 move_to_freelist(page); 1447 if ( _pages_last is null ) 1448 { 1449 _pages_first = null; 1450 } 1451 else 1452 { 1453 _pages_last._nextPage = null; 1454 } 1455 } 1456 } 1457 1458 /// Insert item back. 1459 NodePointer insertBack(T v) { 1460 _length++; 1461 Page* page = _pages_last; 1462 if ( page is null ) 1463 { 1464 page = peek_from_freelist(); 1465 _pages_first = _pages_last = page; 1466 } 1467 if (page._count == NodesPerPage) 1468 { 1469 Page* new_page = peek_from_freelist(); 1470 new_page._prevPage = page; 1471 page._nextPage = new_page; 1472 _pages_last = new_page; 1473 page = new_page; 1474 } 1475 // there is free space 1476 auto index = useFreePosition(page._freeMap); 1477 assert(index < NodesPerPage); 1478 page._nodes[index].v = v; 1479 page._nodes[index].n = -1; 1480 page._nodes[index].p = page._lastNode; 1481 if (page._count == 0) 1482 { 1483 page._firstNode = page._lastNode = cast(ubyte)index; 1484 } 1485 else 1486 { 1487 assert(page._lastNode >= 0); 1488 assert(!isFreePosition(page._freeMap, page._lastNode)); 1489 page._nodes[page._lastNode].n = cast(ubyte)index; 1490 page._lastNode = cast(ubyte)index; 1491 } 1492 page._count++; 1493 assert(page._count == countBusy(page._freeMap)); 1494 debug(cachetools) safe_tracef("page: %s", *page); 1495 return NodePointer(page, index); 1496 } 1497 } 1498 1499 /// 1500 @safe unittest 1501 { 1502 import std.experimental.logger; 1503 import std.algorithm; 1504 import std.range; 1505 1506 globalLogLevel = LogLevel.info; 1507 CompressedList!int list; 1508 foreach(i;0..66) 1509 { 1510 list.insertFront(i); 1511 assert(list.front == i); 1512 } 1513 assert(list.length == 66); 1514 assert(list.back == 0); 1515 list.popFront(); 1516 assert(list.length == 65); 1517 assert(list.front == 64); 1518 list.popFront(); 1519 assert(list.length == 64); 1520 assert(list.front == 63); 1521 while( !list.empty ) 1522 { 1523 list.popFront(); 1524 } 1525 foreach(i;1..19) 1526 { 1527 list.insertFront(i); 1528 assert(list.front == i); 1529 } 1530 foreach(i;1..19) 1531 { 1532 assert(list.back == i); 1533 assert(list.length == 19-i); 1534 list.popBack(); 1535 } 1536 assert(list.empty); 1537 auto p99 = list.insertBack(99); 1538 assert(list.front == 99); 1539 assert(list.back == 99); 1540 auto p100 = list.insertBack(100); 1541 assert(list.front == 99); 1542 assert(list.back == 100); 1543 auto p98 = list.insertFront(98); 1544 auto p101 = list.insertBack(101); 1545 () @trusted // * and remove for poiners is unsafe 1546 { 1547 assert(*p98 == 98); 1548 assert(list.length == 4); 1549 list.remove(p98); 1550 assert(list.length == 3); 1551 assert(list.front == 99); 1552 list.remove(p100); 1553 assert(list.length == 2); 1554 assert(list.front == 99); 1555 assert(list.back == 101); 1556 list.remove(p99); 1557 assert(list.length == 1); 1558 list.clear(); 1559 1560 foreach(i;0..1000) 1561 { 1562 list.insertBack(i); 1563 } 1564 assert(equal(list.range(), iota(0,1000))); 1565 list.clear(); 1566 1567 iota(0, 1000). 1568 map!(i => list.insertBack(i)). 1569 array. 1570 each!(p => list.remove(p)); 1571 assert(list.empty); 1572 iota(0, 1000).map!(i => list.insertBack(i)); 1573 auto r = list.range(); 1574 while(!r.empty) 1575 { 1576 int v = r.front; 1577 r.popFront(); 1578 } 1579 assert(list.empty); 1580 }(); 1581 1582 () @nogc 1583 { 1584 struct S {} 1585 CompressedList!(immutable S) islist; 1586 immutable S s = S(); 1587 islist.insertFront(s); 1588 }(); 1589 class C 1590 { 1591 int c; 1592 this(int v) { 1593 c = v; 1594 } 1595 } 1596 CompressedList!C clist; 1597 foreach(i;0..5000) 1598 { 1599 clist.insertBack(new C(i)); 1600 } 1601 foreach(i;0..4500) 1602 { 1603 clist.popBack(); 1604 } 1605 assert(clist.length == 500); 1606 clist.clear(); 1607 } 1608 1609 // unittest for unsafe types 1610 unittest { 1611 import std.variant; 1612 alias T = Algebraic!(int, string); 1613 auto v = T(1); 1614 CompressedList!T cl; 1615 DList!T dl; 1616 SList!T sl; 1617 cl.insertFront(v); 1618 dl.insertFront(v); 1619 sl.insertFront(v); 1620 assert(cl.front == v); 1621 assert(dl.front.payload == v); 1622 assert(sl.front == v); 1623 cl.insertBack(v); 1624 dl.insertBack(v); 1625 sl.insertBack(v); 1626 cl.popFront; 1627 cl.popBack; 1628 dl.popFront; 1629 dl.popBack; 1630 sl.popFront; 1631 auto a = cl.insertFront(v); 1632 cl.remove(a); 1633 auto b = dl.insertFront(v); 1634 dl.remove(b); 1635 } 1636 1637 @safe @nogc unittest { 1638 import std.range, std.algorithm; 1639 CompressedList!int a, b; 1640 iota(0,100).each!(e => a.insertBack(e)); 1641 a.popFront(); 1642 b = a; 1643 assert(equal(a, b)); 1644 }