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