1 module easyd.list; 2 3 // (C) 2014-2021 by Matthias Rossmy 4 // This file is distributed under the "Fair Use License v2" 5 6 import std.typecons; 7 import std.traits; 8 import std.bigint; 9 import std.stdio; 10 import std.conv; 11 import std.algorithm; 12 13 import easyd.base; 14 15 unittest 16 { 17 class Person 18 { 19 string firstName; 20 string lastName; 21 string likes; 22 23 this(string fn,string ln,string li) 24 { 25 firstName=fn; 26 lastName=ln; 27 likes=li; 28 } 29 30 string name() 31 { 32 return firstName~" "~lastName; 33 } 34 } 35 36 List!Person persons; 37 with(persons) 38 { 39 add(new Person("Walter","Bright","D")); 40 add(new Person("Andrei","Alexandrescu","D")); 41 add(new Person("Bjarne","Stroustrup","C++")); 42 } 43 44 auto selection = persons.selection.filter(p => p.likes=="D").sortAsc(p => p.firstName); 45 assert(selection[0].name=="Andrei Alexandrescu"); 46 assert(selection[1].name=="Walter Bright"); 47 48 auto index = persons.createIndex(p => p.name); 49 auto p = index["Andrei Alexandrescu"]; 50 assert(p.likes=="D"); 51 } 52 53 struct LinkedListDescriptor 54 { 55 ptrdiff_t startLocation = -1; 56 57 void del(TIterator)(ref TIterator it) 58 { 59 it.container.del(this,it); 60 } 61 } 62 63 struct LinkedListPointer(TValue) 64 { 65 ptrdiff_t location; 66 CLinkedListContainer!TValue container; 67 68 this(const CLinkedListContainer!TValue cont, LinkedListDescriptor list) 69 { 70 container = cast(CLinkedListContainer!TValue)cont; 71 location = list.startLocation; 72 } 73 74 this(CLinkedListContainer!TValue cont, ptrdiff_t idx) 75 { 76 container = cont; 77 location = idx; 78 } 79 80 bool hasValue() 81 { 82 return location>=0; 83 } 84 85 ref TValue value() 86 { 87 return container.values[location]; 88 } 89 90 private ptrdiff_t nextLocation() 91 { 92 if(hasValue) 93 { 94 return container.nextLocations[location]; 95 } 96 else 97 { 98 return -1; 99 } 100 } 101 102 private ptrdiff_t prevLocation() 103 { 104 if(hasValue) 105 { 106 return container.prevLocations[location]; 107 } 108 else 109 { 110 return -1; 111 } 112 } 113 } 114 115 struct LinkedListIterator(TValue) 116 { 117 LinkedListPointer!TValue ptr; 118 alias ptr this; 119 120 bool skipNextInc=false; 121 122 this(const CLinkedListContainer!TValue cont, LinkedListDescriptor list) 123 { 124 container = cast(CLinkedListContainer!TValue)cont; 125 location = list.startLocation; 126 } 127 128 this(CLinkedListContainer!TValue cont, ptrdiff_t idx) 129 { 130 container = cont; 131 location = idx; 132 } 133 134 void opUnary(string op)() 135 if (op == "++") 136 { 137 if(!skipNextInc) location = nextLocation(); 138 skipNextInc = false; 139 } 140 141 void opUnary(string op)() 142 if (op == "--") 143 { 144 location = prevLocation(); 145 } 146 } 147 148 class CLinkedListContainer(TValue) 149 { 150 immutable static bool debugMode=false; 151 152 void delegate(LinkedListPointer!TValue)[] beforeDel; 153 TValue[] values; 154 protected ptrdiff_t[] nextLocations; 155 protected ptrdiff_t[] prevLocations; 156 protected ptrdiff_t freeLocation=-1; 157 158 void validate(string info="") 159 { 160 size_t[] hits; 161 ptrdiff_t signedLength = values.length; 162 hits.length = values.length; 163 for(size_t i=0; i<hits.length; i++) hits[i]=0; 164 size_t steps=0; 165 //Check consistency of free list 166 if(freeLocation>=0) //wenn keine freien Elemente im Container, ist freeLocation=-1 167 { 168 ptrdiff_t p = freeLocation; 169 while(p>=0) 170 { 171 hits[p]++; 172 if(nextLocations[p]>=signedLength) 173 { 174 throw new Exception("nextLocations["~p.to!string~"] points outside of the container "~info); 175 } 176 p=nextLocations[p]; 177 if(++steps > values.length+100) 178 { 179 throw new Exception("Infinite loop in a linked list "~info); 180 } 181 } 182 } 183 //Check consistency of real lists 184 static if(debugMode) //without debugMode prevLocations is not maintained for free items, 185 { //so there would be false positive list starts 186 for(size_t i=0; i<hits.length; i++) if(prevLocations[i]<0 && i!=freeLocation) 187 { 188 ptrdiff_t p = i; 189 while(p>=0) 190 { 191 hits[p]++; 192 if(nextLocations[p]>=signedLength) 193 { 194 throw new Exception("nextLocations["~p.to!string~"] points outside of the container "~info); 195 } 196 p=nextLocations[p]; 197 if(++steps > values.length+100) 198 { 199 throw new Exception("Infinite loop in a linked list "~info); 200 } 201 } 202 } 203 } 204 //Check that every item belongs to exactly 1 list 205 for(size_t i=0; i<hits.length; i++) 206 { 207 static if(debugMode) //without DebugMode the "lists test" must be skipped, so not every item may have been hit 208 { 209 if(hits[i]==0) 210 { 211 throw new Exception("Container item "~i.to!string~" is not reachable "~info); 212 } 213 } 214 if(hits[i]> 1) 215 { 216 throw new Exception("Container item "~i.to!string~" is used by multiple lists "~info); 217 } 218 } 219 } 220 221 void clear() 222 { 223 if(values.length==0) return; 224 225 for(ptrdiff_t pos=0; pos<values.length; pos++) 226 { 227 static if(is(TValue:Object) || isDynamicArray!TValue || isAssociativeArray!TValue) 228 { 229 values[pos] = null; 230 } 231 nextLocations[pos] = pos + 1; 232 static if(debugMode) prevLocations[pos] = pos - 1; 233 } 234 nextLocations[values.length-1] = -1; 235 236 freeLocation = 0; 237 238 static if(debugMode) validate; 239 } 240 241 void addAnywhere(ref LinkedListDescriptor list, TValue val) 242 { 243 //insert after 1st item, because that requires no searching 244 if(freeLocation>=0 && list.startLocation==freeLocation) 245 { 246 throw new Exception("Start location of list conflicts with freeLocation ("~freeLocation.to!string~")"); 247 } 248 addAfter(list,list.startLocation,val); 249 } 250 251 ptrdiff_t addAfter(ref LinkedListDescriptor list, ptrdiff_t prevLocation, TValue val) 252 { 253 //writeln("addAfter " ~ prevLocation.to!string); 254 auto pos = addHelper(val); 255 //writeln("DestPos: " ~ pos.to!string); 256 257 if(list.startLocation<0) 258 { 259 //writeln("create 1-item-list"); 260 nextLocations[pos] = -1; 261 prevLocations[pos] = -1; 262 list.startLocation = pos; 263 264 static if(debugMode) validate; //use 3 different validate calls in this function, so that the backtrace shows which case failed 265 } 266 else if (prevLocation<0) 267 { 268 //writeln("insert at beginning"); 269 auto nextLocation = list.startLocation; 270 link(pos,nextLocation); 271 prevLocations[pos] = -1; 272 list.startLocation = pos; 273 274 static if(debugMode) validate; 275 } 276 else 277 { 278 //writeln("insert into list"); 279 auto nextLocation = nextLocations[prevLocation]; 280 link(prevLocation,pos); 281 link(pos,nextLocation); 282 283 static if(debugMode) validate("prevLocation="~prevLocation.to!string~" pos="~pos.to!string~" nextLocation="~nextLocation.to!string); 284 } 285 286 //writeln("Finished addAfter"); 287 return pos; 288 } 289 290 void link(ptrdiff_t pos1,ptrdiff_t pos2) 291 { 292 if(pos1>=0) nextLocations[pos1] = pos2; 293 if(pos2>=0) prevLocations[pos2] = pos1; 294 } 295 296 ptrdiff_t del(ref LinkedListDescriptor list, LinkedListPointer!TValue it) //returns location of next item 297 { 298 if(it.container!=this) 299 { 300 throw new Exception("LinkedListPointer not from this container"); 301 } 302 303 beforeDel.trigger(it); 304 305 auto nextLocation = it.nextLocation; 306 auto prevLocation = it.prevLocation; 307 308 static if(debugMode) if((it.location==list.startLocation)!=(prevLocation==-1)) 309 { 310 if(prevLocation==-1) 311 { 312 throw new Exception("Iterator has no predecessor, but it is not at the start location of the list"); 313 }else{ 314 throw new Exception("Iterator has a predecessor, but it is at the start location of the list"); 315 } 316 } 317 318 //writeln("prev=",prevLocation," next=",nextLocation); 319 320 //Wert zurücksetzen, damit der GC keine unnötigen Objekte behält 321 static if(is(TValue==class) || isDynamicArray!TValue) 322 { 323 values[it.location] = null; 324 } 325 326 //eigene Liste wieder zusammenfügen 327 if(it.location==list.startLocation) 328 { 329 //writeln("Change list start"); 330 list.startLocation = nextLocation; 331 link(-1,nextLocation); 332 } 333 else 334 { 335 link(prevLocation,nextLocation); 336 } 337 338 //frei gewordenes Item am Anfang der Frei-Liste einfügen 339 link(it.location,freeLocation); 340 prevLocations[it.location] = -1; 341 freeLocation = it.location; 342 343 static if(debugMode) validate; 344 345 return nextLocation; 346 } 347 348 void del(ref LinkedListDescriptor list, ref LinkedListIterator!TValue it) 349 { 350 it.location = del(list,it.ptr); 351 it.skipNextInc = true; 352 } 353 354 LinkedListIterator!TValue iterator(LinkedListDescriptor list) const 355 { 356 return LinkedListIterator!TValue(this,list); 357 } 358 359 protected ptrdiff_t addHelper(TValue val) 360 { 361 if(freeLocation<0) increaseSize(); 362 363 ptrdiff_t pos = freeLocation; 364 freeLocation = nextLocations[pos]; 365 values[pos] = val; 366 367 return pos; 368 } 369 370 protected void increaseSize() 371 { 372 immutable size_t maxInitialSize=4; 373 374 if(freeLocation>=0) 375 { 376 return; 377 } 378 379 auto oldsize = values.length; 380 if(oldsize==0) 381 { 382 auto initialsize = 1024 / max(TValue.sizeof,ptrdiff_t.sizeof); //initial max. 1 KB pro Sub-Array 383 if(initialsize<1) initialsize=1; 384 if(initialsize>maxInitialSize) initialsize=maxInitialSize; 385 values.length = initialsize; 386 nextLocations.length = initialsize; 387 prevLocations.length = initialsize; 388 } 389 else if(oldsize<=MEBI) 390 { 391 values.length *= 2; 392 nextLocations.length *= 2; 393 prevLocations.length *= 2; 394 } 395 else 396 { 397 values.length += MEBI; 398 nextLocations.length += MEBI; 399 prevLocations.length += MEBI; 400 } 401 402 for(ptrdiff_t pos=oldsize; pos<values.length; pos++) 403 { 404 nextLocations[pos] = pos + 1; 405 static if(debugMode) prevLocations[pos] = pos - 1; 406 } 407 static if(debugMode) prevLocations[oldsize] = -1; 408 nextLocations[values.length-1] = -1; 409 410 freeLocation = oldsize; 411 412 static if(debugMode) validate; 413 } 414 } 415 416 struct LinkedListContainer(TValue) 417 { 418 mixin ImplementStruct!(CLinkedListContainer!TValue); 419 } 420 421 mixin template CListIndexOperations(T,TKey) 422 { 423 ListIndex!(T,TKey) mainIndex; 424 425 LinkedListPointer!T pointerOf(TKey key) 426 { 427 if(mainIndex is null) mainIndex = createMainIndex; 428 return mainIndex(key); 429 } 430 431 Selection!T all(TKey key) 432 { 433 if(mainIndex is null) mainIndex = createMainIndex; 434 return mainIndex.all(key); 435 } 436 437 bool contains(TKey key) 438 { 439 return pointerOf(key).hasValue; 440 } 441 442 void delFirst(TKey key) 443 { 444 auto it = pointerOf(key); 445 if(it.hasValue) 446 { 447 del(it); 448 } 449 } 450 451 void delAll(TKey key) 452 { 453 foreach(pos,it,val;all(key)) del(it); 454 } 455 456 static if(isAggregateType!T && !isTuple!T) 457 { 458 immutable string idfield = idField!T; 459 460 ref T opIndex(TKey key) 461 { 462 auto it = pointerOf(key); 463 if(it.hasValue) 464 { 465 return it.value; 466 }else{ 467 return mainIndex.defaultValue; 468 } 469 } 470 471 static if(idfield!="") 472 { 473 ref T opIndex(TKey key, T def) 474 { 475 auto it = pointerOf(key); 476 if(!it.hasValue) 477 { 478 it = add(def); 479 auto ptr = &(it.value()); 480 __traits(getMember, *ptr, idfield) = key; 481 } 482 return it.value; 483 } 484 } 485 } 486 } 487 488 class CList(T) //: ISpecialSerialize 489 { 490 size_t length = 0; 491 bool valuesCanChange; 492 CLinkedListContainer!T container; 493 protected LinkedListDescriptor list; 494 protected ptrdiff_t lastLocation; 495 protected IListIndex[] indexes; 496 497 static if(isAggregateType!T && !isTuple!T) 498 { 499 static if(idField!T != "") 500 { 501 immutable string idfield = idField!T; 502 alias typeof(__traits(getMember, T, idfield)) TKey; 503 504 ListIndex!(T, TKey) createMainIndex() 505 { 506 return createIndex(item=>__traits(getMember, item, idfield),!valuesCanChange); 507 } 508 509 mixin CListIndexOperations!(T,TKey); 510 }else{ 511 ListIndex!(T,T) createMainIndex() 512 { 513 return createIndex(item=>item,!valuesCanChange); 514 } 515 516 mixin CListIndexOperations!(T,T); 517 518 void addIfNew(T item) 519 { 520 if(!pointerOf(item).hasValue) add(item); 521 } 522 } 523 }else{ //simple type or tuple 524 ListIndex!(T,T) createMainIndex() 525 { 526 return createIndex(item=>item,!valuesCanChange); 527 } 528 529 mixin CListIndexOperations!(T,T); 530 531 void addIfNew(T item) 532 { 533 if(!pointerOf(item).hasValue) add(item); 534 } 535 } 536 537 this(bool valuescanchange = true) 538 { 539 container.create; 540 valuesCanChange = valuescanchange; 541 } 542 543 void check() 544 { 545 ptrdiff_t last=-1; 546 for(auto it=iterator;it.hasValue;it++) 547 { 548 if(it.prevLocation!=last) throw new Exception("Linked list broken, last item was "~last.to!string~" but prevLocation of "~it.location.to!string~" points to "~it.prevLocation.to!string); 549 last = it.location; 550 } 551 writeln("List-links ok"); 552 } 553 554 CList!T dup() 555 { 556 auto result = new CList!T; 557 foreach(it,item;this) result.add(it.value); 558 return result; 559 } 560 561 void clear() 562 { 563 container.clear; 564 list.startLocation = -1; 565 length=0; 566 foreach(index;indexes) 567 { 568 index.invalidate; 569 } 570 } 571 572 LinkedListPointer!T add(T item) 573 { 574 lastLocation = container.addAfter(list,lastLocation,item); 575 addHelper; 576 return LinkedListPointer!T(container, lastLocation); 577 } 578 579 LinkedListPointer!T addBefore(LinkedListPointer!T before, T newItem) 580 { 581 //writeln("addBefore"); 582 if(!before.hasValue) 583 { 584 //writeln("Can't addBefore, doing normal add"); 585 add(newItem); 586 } 587 588 auto result = LinkedListPointer!T(container, container.addAfter(list,before.prevLocation,newItem)); 589 addHelper; 590 return result; 591 } 592 593 LinkedListPointer!T addAfter(LinkedListPointer!T after, T newItem) 594 { 595 //writeln("addAfter"); 596 if(!after.hasValue) 597 { 598 //writeln("Can't addAfter, doing normal add"); 599 add(newItem); 600 } 601 602 auto result = LinkedListPointer!T(container, container.addAfter(list,after.location,newItem)); 603 addHelper; 604 return result; 605 } 606 607 static if(is(T==class) || is(T==interface)) 608 { 609 T2 addNew(T2=T,P...)(P par) 610 { 611 T2 obj = new T2(par); 612 add(obj); 613 return obj; 614 } 615 } 616 else 617 { 618 T* addNew() 619 { 620 add(T.init); 621 return &(container.values[lastLocation]); 622 } 623 } 624 625 /*void replace(LinkedListPointer!T it, T newValue) 626 { 627 container.values[it.location] = newValue; 628 //TODO: Indices aktualisieren 629 }*/ 630 631 ptrdiff_t del(LinkedListPointer!T it) //returns location of next item 632 { 633 //writeln("list.del ",this," ",it.location); 634 if(it.location<0) 635 { 636 return -1; 637 } 638 if(it.location==lastLocation) 639 { 640 lastLocation = it.prevLocation; 641 } 642 if(indexes !is null) 643 { 644 //writeln("Updating indexes..."); 645 foreach(index;indexes) 646 { 647 index.autoNotifyBeforeDelete(it.location); 648 } 649 //writeln("...finished updating indexes"); 650 } 651 length--; 652 return container.del(list,it); 653 } 654 655 void del(ref LinkedListIterator!T it) 656 { 657 if(it.container!=container) return; 658 it.location = del(it.ptr); 659 it.skipNextInc = true; 660 } 661 662 void delFirstMatch(bool delegate(T) sd) 663 { 664 for(auto it=iterator; it.hasValue; it++) 665 { 666 if(sd(it.value)) 667 { 668 del(it); 669 return; 670 } 671 } 672 } 673 674 void delAllMatches(bool delegate(T) sd) 675 { 676 for(auto it=iterator; it.hasValue; it++) 677 { 678 if(sd(it.value)) 679 { 680 del(it); 681 } 682 } 683 } 684 685 LinkedListIterator!T iterator() const 686 { 687 return container.iterator(list); 688 } 689 690 LinkedListIterator!T lastIterator() 691 { 692 return LinkedListIterator!T(container,lastLocation); 693 } 694 695 ref T first() 696 { 697 return iterator.value; 698 } 699 700 static if(isRefType!T) 701 { 702 T itemAt(size_t number) 703 { 704 if(number>=length) return null; 705 auto result = iterator; 706 for(size_t x=0; x<number; x++) result++; 707 return result.value; 708 } 709 }else{ 710 ref T itemAt(size_t number) 711 { 712 if(number>=length) throw new Exception("Requesting item out of range"); 713 auto result = iterator; 714 for(size_t x=0; x<number; x++) result++; 715 return result.value; 716 } 717 } 718 719 static if(is(T==class) || is(T==interface)) // class ///////////////////////////////////////////////// 720 { 721 //pragma(msg,typeid(T)," is class"); 722 723 int opApply(int delegate(T) dg) const 724 { 725 int result = 0; 726 for (auto it = iterator; it.hasValue; it++) 727 { 728 result = dg(it.value); 729 if (result) break; 730 } 731 return result; 732 } 733 734 int opApply(int delegate(ref LinkedListIterator!T, T) dg) const 735 { 736 int result = 0; 737 for (auto it = iterator; it.hasValue; it++) 738 { 739 result = dg(it,it.value); 740 if (result) break; 741 } 742 return result; 743 } 744 } 745 else static if(__traits(hasMember, T, "tupleof")) // struct //////////////////////////////////////////// 746 { 747 //pragma(msg,typeid(T)," is struct"); 748 749 int opApply(int delegate(T*) dg) const 750 { 751 int result = 0; 752 for (auto it = iterator; it.hasValue; it++) 753 { 754 result = dg(cast(T*)&(container.values[it.location])); 755 if (result) break; 756 } 757 return result; 758 } 759 760 int opApply(int delegate(ref LinkedListIterator!T, T*) dg) const 761 { 762 int result = 0; 763 for (auto it = iterator; it.hasValue; it++) 764 { 765 result = dg(it,cast(T*)&(container.values[it.location])); 766 if (result) break; 767 } 768 return result; 769 } 770 } 771 else // simple type /////////////////////////////////////////////////////////////////////////////////// 772 { 773 //pragma(msg,typeid(T)," is simple type"); 774 775 int opApply(int delegate(const T) dg) const 776 { 777 int result = 0; 778 for (auto it = iterator; it.hasValue; it++) 779 { 780 result = dg(it.value); 781 if (result) break; 782 } 783 return result; 784 } 785 786 int opApply(int delegate(ref LinkedListIterator!T, const T) dg) const 787 { 788 int result = 0; 789 for (auto it = iterator; it.hasValue; it++) 790 { 791 result = dg(it,it.value); 792 if (result) break; 793 } 794 return result; 795 } 796 } 797 798 Selection!T selection() const 799 { 800 return new Selection!T(this,true); 801 } 802 //alias selection this; 803 804 ListIndex!(T,TKey) createIndex(TKey)(TKey delegate(T) sd, bool constField=false, size_t estimListLength=0) 805 { 806 auto result = new ListIndex!(T,TKey)(this,sd,constField,estimListLength); 807 indexes ~= result; 808 return result; 809 } 810 811 bool found(bool delegate(T) sd) //darf nicht contains heißen wegen Konflikt mit CListIndexOperations 812 { 813 return find(sd).hasValue; 814 } 815 816 LinkedListPointer!T find(bool delegate(T) sd) 817 { 818 for(auto it=iterator; it.hasValue; it++) 819 { 820 if(sd(it.value)) return it; 821 } 822 return LinkedListPointer!T(container,-1); 823 } 824 825 T2 find(T2)(bool delegate(T2) sd) 826 if(is(T2==class)) 827 { 828 foreach(item;this) 829 { 830 if(item.inherits!T2) 831 { 832 T2 t = cast(T2)item; 833 if(sd(t)) return t; 834 } 835 } 836 return T2.init; 837 } 838 839 LinkedListPointer!T findMax(TMember)(TMember delegate(T) sd) 840 { 841 auto it=iterator; 842 auto mx = sd(it.value); 843 auto result = it.ptr; 844 for(it++; it.hasValue; it++) 845 { 846 auto v = sd(it.value); 847 if(v>mx) 848 { 849 mx = v; 850 result = it.ptr; 851 } 852 } 853 return result; 854 } 855 856 /* TODO for DocMan 857 void bubbleUp(LinkedListPointer!T it) 858 { 859 container.bubbleUp(list,it); 860 } 861 862 void bubbleDown(LinkedListPointer!T it) 863 { 864 container.bubbleDown(list,it); 865 } 866 */ 867 868 CList!T opBinary(string op)(CList!T other) 869 { 870 static if (op == "~") 871 { 872 auto result = dup; 873 foreach(item;other) result.add(item); 874 return result; 875 }else{ 876 assert(0, "Operator "~op~" not implemented"); 877 } 878 } 879 880 /*void serialize(ISerializer destination) 881 { 882 //writeln("List serialize"); 883 foreach(it,item;this) 884 { 885 destination.serializeSubItem("",it.value); 886 } 887 } 888 889 bool deserialize(IDeserializer source) 890 { 891 foreach(child;source.current.children) 892 { 893 child.allowUnnamed; 894 auto newitem = source.deserialize!T(child); 895 static if(__traits(hasMember, T, "_first_member_inline_")) 896 { 897 newitem.tupleof[0] = StringConvert!(typeof(newitem.tupleof[0])).parse(child.value); 898 } 899 add(newitem); 900 } 901 return true; 902 }*/ 903 904 void compact() //ungenutzten Puffer abschneiden, nicht defragmentieren 905 { 906 ptrdiff_t maxloc=0; 907 for(auto it=iterator;it.hasValue;it++) if(it.location>maxloc) maxloc=it.location; 908 container.values.length = maxloc+1; 909 container.nextLocations.length = maxloc+1; 910 container.prevLocations.length = maxloc+1; 911 if(container.freeLocation>=container.values.length) container.freeLocation=-1; 912 } 913 914 protected void addHelper() 915 { 916 length++; 917 if(indexes !is null) 918 { 919 //writeln("Updating indexes..."); 920 foreach(index;indexes) 921 { 922 index.autoNotifyAdded(lastLocation); 923 } 924 //writeln("...finished updating indexes"); 925 } 926 } 927 } 928 929 struct List(T) 930 { 931 mixin ImplementStruct!(CList!T); 932 } 933 934 string concat(T)(CList!T l, string separator="", string delegate(T) tostr = item=>item.to!string) 935 { 936 string result; 937 for(auto it=l.iterator; it.hasValue; it++) 938 { 939 result ~= tostr(it.value); 940 if(it.nextLocation>=0) result ~= separator; 941 } 942 return result; 943 } 944 945 class Selection(T) 946 { 947 bool sorted = false; 948 protected T[] source; 949 protected CList!T sourceList; 950 protected size_t[] locations; 951 952 this(const T[] src, bool addAll=false) 953 { 954 source = cast(T[])src; 955 if(addAll) 956 { 957 for(size_t x=0; x<source.length; x++) 958 { 959 add(x); 960 } 961 } 962 } 963 964 this(const CList!T src, bool addAll=false) 965 { 966 source = cast(T[])(src.container.values); 967 sourceList = cast(CList!T)src; 968 if(addAll) 969 { 970 foreach(it,val;src) 971 { 972 add(it.location); 973 } 974 } 975 } 976 977 size_t length() 978 { 979 return locations.length; 980 } 981 982 Selection!T filter(bool delegate(T) condition) 983 { 984 size_t[] newlocations; 985 foreach(l;locations) 986 { 987 if(condition(source[l])) newlocations ~= l; 988 } 989 locations = newlocations; 990 return this; 991 } 992 993 Selection!T sortAsc(TKey)(TKey delegate(T) select) 994 { 995 if(sorted) 996 { 997 sort!((i1,i2) => select(source[i1])<select(source[i2]), SwapStrategy.stable)(locations); 998 } 999 else 1000 { 1001 sort!((i1,i2) => select(source[i1])<select(source[i2]), SwapStrategy.unstable)(locations); 1002 } 1003 sorted = true; 1004 return this; 1005 } 1006 1007 Selection!T sortDesc(TKey)(TKey delegate(T) select) 1008 { 1009 if(sorted) 1010 { 1011 sort!((i1,i2) => select(source[i1])>select(source[i2]), SwapStrategy.stable)(locations); 1012 } 1013 else 1014 { 1015 sort!((i1,i2) => select(source[i1])>select(source[i2]), SwapStrategy.unstable)(locations); 1016 } 1017 sorted = true; 1018 return this; 1019 } 1020 1021 ref T opIndex(size_t pos) 1022 { 1023 return source[locations[pos]]; 1024 } 1025 1026 static if(__traits(hasMember, T, "tupleof")) 1027 { 1028 static if(is(T==class) || is(T==interface) || isTuple!T) 1029 { 1030 int opApply(int delegate(T) dg) 1031 { 1032 int result = 0; 1033 foreach(location;locations) 1034 { 1035 result = dg(source[location]); 1036 if (result) break; 1037 } 1038 return result; 1039 } 1040 1041 int opApply(int delegate(ptrdiff_t,T) dg) 1042 { 1043 int result = 0; 1044 foreach(index,location;locations) 1045 { 1046 result = dg(index,source[location]); 1047 if (result) break; 1048 } 1049 return result; 1050 } 1051 1052 int opApply(int delegate(ptrdiff_t,LinkedListIterator!T, T) dg) 1053 { 1054 if(sourceList is null) 1055 { 1056 throw new Exception("Foreach with LinkedListIterator is not allowed on arrays"); 1057 } 1058 int result = 0; 1059 foreach(index,location;locations) 1060 { 1061 result = dg(index,LinkedListIterator!T(sourceList.container,location),source[location]); 1062 if (result) break; 1063 } 1064 return result; 1065 } 1066 } 1067 else //struct 1068 { 1069 int opApply(int delegate(T*) dg) 1070 { 1071 int result = 0; 1072 foreach(location;locations) 1073 { 1074 result = dg(&(source[location])); 1075 if (result) break; 1076 } 1077 return result; 1078 } 1079 1080 int opApply(int delegate(ptrdiff_t,T*) dg) 1081 { 1082 int result = 0; 1083 foreach(index,location;locations) 1084 { 1085 result = dg(index,&(source[location])); 1086 if (result) break; 1087 } 1088 return result; 1089 } 1090 1091 int opApply(int delegate(ptrdiff_t,LinkedListIterator!T, T*) dg) 1092 { 1093 if(sourceList is null) 1094 { 1095 throw new Exception("Foreach with LinkedListIterator is not allowed on arrays"); 1096 } 1097 int result = 0; 1098 foreach(index,location;locations) 1099 { 1100 result = dg(index,LinkedListIterator!T(sourceList.container,location),&(source[location])); 1101 if (result) break; 1102 } 1103 return result; 1104 } 1105 } 1106 }else{ //single value 1107 int opApply(int delegate(const T) dg) 1108 { 1109 int result = 0; 1110 foreach(location;locations) 1111 { 1112 result = dg(source[location]); 1113 if (result) break; 1114 } 1115 return result; 1116 } 1117 1118 int opApply(int delegate(ptrdiff_t,const T) dg) 1119 { 1120 int result = 0; 1121 foreach(index,location;locations) 1122 { 1123 result = dg(index,source[location]); 1124 if (result) break; 1125 } 1126 return result; 1127 } 1128 1129 int opApply(int delegate(ptrdiff_t,LinkedListIterator!T, const T) dg) 1130 { 1131 if(sourceList is null) 1132 { 1133 throw new Exception("Foreach with LinkedListIterator is not allowed on arrays"); 1134 } 1135 int result = 0; 1136 foreach(index,location;locations) 1137 { 1138 result = dg(index,LinkedListIterator!T(sourceList.container,location),source[location]); 1139 if (result) break; 1140 } 1141 return result; 1142 } 1143 } 1144 1145 protected void add(size_t location) 1146 { 1147 locations ~= location; 1148 } 1149 } 1150 1151 abstract class IListIndex 1152 { 1153 protected LinkedListDescriptor[] hashTable; 1154 protected CLinkedListContainer!ptrdiff_t locationContainer; 1155 1156 protected abstract ulong hashOfLocation(ptrdiff_t location); 1157 1158 void autoNotifyAdded(ptrdiff_t location) 1159 { 1160 //writeln("autoNotifyAdded " ~ location.to!string); 1161 if(hashTable !is null) 1162 { 1163 locationContainer.addAnywhere(hashTable[hashOfLocation(location) % hashTable.length],location); 1164 //writeln("Added item at " ~ location.to!string ~ " to index"); 1165 } 1166 } 1167 1168 void autoNotifyBeforeDelete(ptrdiff_t location) 1169 { 1170 if(hashTable !is null) 1171 { 1172 auto loclist = hashTable[hashOfLocation(location) % hashTable.length]; 1173 for(auto it=locationContainer.iterator(loclist); it.hasValue; it++) 1174 { 1175 if(it.value == location) 1176 { 1177 locationContainer.del(loclist,it); 1178 //writeln("Deleted item "~location.to!string~" from index"); 1179 return; 1180 } 1181 } 1182 } 1183 } 1184 1185 void invalidate() 1186 { 1187 hashTable = null; 1188 locationContainer = null; 1189 } 1190 } 1191 1192 size_t hash(BigInt x) 1193 { 1194 return x.toLong; 1195 } 1196 1197 size_t hash(T)(T x) 1198 if(!is(T:BigInt)) 1199 { 1200 return x.toHash; 1201 } 1202 1203 class IConstHashObject //this is needed, because Object.toHash is based on the address, which might change on garbage collections 1204 { 1205 private ulong hash; //TODO: make @DontSerialize 1206 private static ulong nextHash=0; 1207 1208 this() 1209 { 1210 hash = nextHash++; 1211 } 1212 1213 override ulong toHash() 1214 { 1215 return hash; 1216 } 1217 } 1218 1219 class ListIndex(TItem,TKey) : IListIndex 1220 { 1221 static immutable size_t defaultHashTableSize = 10240; 1222 size_t maxIterations = 10; 1223 static immutable float hashTableBasicSizeFactor = 1; 1224 static immutable float hashTableComplexSizeFactor = 3; 1225 bool constField; 1226 size_t estimatedListLength; 1227 float hashTableSizeFactor; 1228 TItem defaultValue; 1229 protected CList!TItem list; 1230 protected TKey delegate(TItem) select; 1231 1232 void _do_not_serialize_(){} 1233 1234 protected this(CList!TItem li, TKey delegate(TItem) sd, bool constField=false, size_t estimListLength=0) 1235 { 1236 //writeln("Creating list index..."); 1237 list = li; 1238 select = sd; 1239 this.constField = constField; 1240 estimatedListLength = estimListLength; 1241 static if(isBasicType!TKey) 1242 { 1243 hashTableSizeFactor = hashTableBasicSizeFactor; 1244 } 1245 else 1246 { 1247 hashTableSizeFactor = hashTableComplexSizeFactor; 1248 } 1249 } 1250 1251 LinkedListPointer!TItem opCall(TKey key) 1252 { 1253 //writeln("Searching for ",key); 1254 checkHashTable; 1255 1256 if(hashTable !is null) 1257 { 1258 //über hashTable suchen 1259 //writeln("Searching via Hash-Table"); 1260 size_t hashentry = hash(key) % hashTable.length; 1261 for (auto it=locationContainer.iterator(hashTable[hashentry]); it.hasValue; it++) 1262 { 1263 auto itemkey = select(list.container.values[it.value]); 1264 if (itemkey == key) 1265 { 1266 return LinkedListPointer!TItem(list.container,it.value); 1267 } 1268 if(!constField) 1269 { 1270 if((hash(itemkey)%hashTable.length) != hashentry) //Key wurde geändert 1271 { 1272 writeln("Key wurde geändert und Item wird neu in ListIndex einsortiert"); 1273 autoNotifyAdded(it.value); 1274 locationContainer.del(hashTable[hashentry],it); 1275 } 1276 } 1277 } 1278 } 1279 1280 if(hashTable is null || !constField) 1281 { 1282 //iterieren 1283 //writeln("Searching via Iteration over ",list.length," items"); 1284 for (auto it=list.iterator; it.hasValue; it++) 1285 { 1286 if (select(it.value) == key) 1287 { 1288 return it; 1289 } 1290 } 1291 } 1292 1293 //nicht gefunden => return -1; 1294 //writeln("Not found"); 1295 return LinkedListPointer!TItem(list.container,-1); 1296 } 1297 1298 ref TItem opIndex(TKey key) 1299 { 1300 auto it = opCall(key); 1301 if(it.hasValue) 1302 { 1303 return it.value; 1304 }else{ 1305 return defaultValue; 1306 } 1307 } 1308 1309 ref TItem opIndex(TKey key, TItem def) 1310 { 1311 auto it = opCall(key); 1312 if(it.hasValue) 1313 { 1314 return it.value; 1315 }else{ 1316 return list.add(def).value; 1317 } 1318 } 1319 1320 bool contains(TKey key) 1321 { 1322 return opCall(key).hasValue; 1323 } 1324 1325 Selection!TItem all(TKey key) 1326 { 1327 if(constField) checkHashTable; 1328 auto result = new Selection!TItem(list); 1329 1330 if(hashTable is null || !constField) 1331 { 1332 //iterieren 1333 for (auto it=list.iterator; it.hasValue; it++) 1334 { 1335 if (select(it.value) == key) 1336 { 1337 result.add(it.location); 1338 } 1339 } 1340 } 1341 else 1342 { 1343 //über hashTable suchen 1344 auto hashCode = hash(key); 1345 for (auto it=locationContainer.iterator(hashTable[hashCode % hashTable.length]); it.hasValue; it++) 1346 { 1347 if (select(list.container.values[it.value]) == key) 1348 { 1349 result.add(it.value); 1350 } 1351 } 1352 } 1353 1354 return result; 1355 } 1356 1357 void update(TItem item) 1358 { 1359 auto it = opCall(select(item)); 1360 if(it.hasValue) 1361 { 1362 it.value = item; 1363 }else{ 1364 list.add(item); 1365 } 1366 } 1367 1368 TKey key(LinkedListPointer!TItem item) 1369 { 1370 return select(item.value); 1371 } 1372 1373 void notifyKeyChanged(TKey oldKey, LinkedListPointer!TItem item) 1374 { 1375 if(hashTable !is null) 1376 { 1377 auto hashtablepos = hash(oldKey) % hashTable.length; 1378 for(auto it=locationContainer.iterator(hashTable[hashtablepos]); it.hasValue; it++) 1379 { 1380 if(it.value == item.location) 1381 { 1382 locationContainer.del(hashTable[hashtablepos],it); 1383 //writeln("Refreshing item "~item.location.to!string~" in index"); 1384 break; 1385 } 1386 } 1387 autoNotifyAdded(item.location); 1388 } 1389 } 1390 1391 protected override ulong hashOfLocation(ptrdiff_t location) 1392 { 1393 //writeln("hashOfLocation..."); 1394 return hash(select(list.container.values[location])); 1395 } 1396 1397 protected void checkHashTable() 1398 { 1399 if(hashTable is null && list.length>maxIterations) 1400 { 1401 if(estimatedListLength>0) 1402 { 1403 hashTable.length = (max(estimatedListLength,list.length) * hashTableSizeFactor).to!size_t; 1404 } 1405 else 1406 { 1407 hashTable.length = max((list.length * hashTableSizeFactor).to!size_t, defaultHashTableSize); 1408 } 1409 //writeln("Created hash table with " ~ hashTable.length.to!string ~ " entries."); 1410 locationContainer = new CLinkedListContainer!ptrdiff_t; 1411 1412 for (auto it=list.iterator; it.hasValue; it++) 1413 { 1414 autoNotifyAdded(it.location); 1415 } 1416 } 1417 } 1418 }