1 /** 2 * T-Tree. 3 * Copyright: © 2015 Economic Modeling Specialists, Intl. 4 * Authors: Brian Schott 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 8 module containers.ttree; 9 10 private import containers.internal.node : shouldAddGCRange; 11 private import containers.internal.mixins : AllocatorState; 12 private import std.experimental.allocator.mallocator : Mallocator; 13 14 /** 15 * Implements a binary search tree with multiple items per tree node. 16 * 17 * T-tree Nodes are (by default) sized to fit within a 64-byte 18 * cache line. The number of items stored per node can be read from the 19 * `nodeCapacity` enum. Each node has 0, 1, or 2 children. Each node has between 20 * 1 and `nodeCapacity` items, or it has `nodeCapacity` items and 0 or 21 * more children. 22 * 23 * Inserting or removing items while iterating a range returned from `opSlice`, 24 * `upperBound`, `equalRange`, or other similar functions will result in 25 * unpredicable and likely invalid iteration orders. 26 * 27 * Params: 28 * T = the element type 29 * Allocator = the allocator to use. Defaults to `Mallocator`. 30 * allowDuplicates = if true, duplicate values will be allowed in the tree 31 * less = the comparitor function to use 32 * supportGC = true if the container should support holding references to 33 * GC-allocated memory. 34 * cacheLineSize = Nodes will be sized to fit within this number of bytes. 35 * See_also: $(LINK http://en.wikipedia.org/wiki/T-tree) 36 */ 37 struct TTree(T, Allocator = Mallocator, bool allowDuplicates = false, 38 alias less = "a < b", bool supportGC = shouldAddGCRange!T, size_t cacheLineSize = 64) 39 { 40 /** 41 * T-Trees are not copyable due to the way they manage memory and interact 42 * with allocators. 43 */ 44 this(this) @disable; 45 46 static if (stateSize!Allocator != 0) 47 { 48 /// No default construction if an allocator must be provided. 49 this() @disable; 50 51 /** 52 * Use `allocator` to allocate and free nodes in the tree. 53 */ 54 this(Allocator allocator) 55 in 56 { 57 static if (is(typeof(allocator is null))) 58 assert(allocator !is null, "Allocator must not be null"); 59 } 60 do 61 { 62 this.allocator = allocator; 63 } 64 65 private alias AllocatorType = Allocator; 66 } 67 else 68 private alias AllocatorType = void*; 69 70 ~this() @trusted 71 { 72 scope(failure) assert(false, "TTree destructor threw an exception"); 73 clear(); 74 } 75 76 /** 77 * Removes all elements from the tree. 78 */ 79 void clear() 80 { 81 _length = 0; 82 if (root is null) 83 return; 84 static if (stateSize!Allocator > 0) 85 deallocateNode(root, allocator); 86 else 87 deallocateNode(root, null); 88 } 89 90 debug(EMSI_CONTAINERS) invariant() 91 { 92 assert (root is null || _length != 0, "Empty tree with non-null root"); 93 } 94 95 /** 96 * $(B tree ~= item) operator overload. 97 */ 98 void opOpAssign(string op)(T value) if (op == "~") 99 { 100 insert(value); 101 } 102 103 /** 104 * Inserts the given value(s) into the tree. 105 * 106 * This is not a stable insert. You will get strange results if you insert 107 * into a tree while iterating over it. 108 * 109 * Params: 110 * value = the value to insert 111 * overwrite = if `true` the given `value` will replace the first item 112 * in the tree that is equivalent (That is greater-than and less-than 113 * are both false) to `value`. This is useful in cases where opCmp 114 * and opEquals for `T` type have different meanings. For example, 115 * if the element type is a circle that has a position and a color, 116 * the circle could implement `opCmp` to sort by color, and calling 117 * `insert` with `overwrite` set to `true` would allow you to update 118 * the position of the circle with a certain color in the tree. 119 * Returns: the number of values added. 120 */ 121 size_t insert(T value, bool overwrite = false) @safe 122 { 123 if (root is null) 124 { 125 static if (stateSize!Allocator > 0) 126 root = allocateNode(cast(Value) value, null, allocator); 127 else 128 root = allocateNode(cast(Value) value, null, null); 129 ++_length; 130 return true; 131 } 132 static if (stateSize!Allocator > 0) 133 immutable bool r = root.insert(cast(Value) value, root, allocator, overwrite); 134 else 135 immutable bool r = root.insert(cast(Value) value, root, null, overwrite); 136 if (r) 137 ++_length; 138 return r ? 1 : 0; 139 } 140 141 /// ditto 142 size_t insert(R)(R r, bool overwrite = false) if (isInputRange!R && is(ElementType!R == T)) 143 { 144 size_t retVal; 145 while (!r.empty) 146 { 147 retVal += insert(r.front(), overwrite); 148 r.popFront(); 149 } 150 return retVal; 151 } 152 153 /// ditto 154 size_t insert(T[] values, bool overwrite = false) 155 { 156 size_t retVal; 157 foreach (ref v; values) 158 retVal += insert(v, overwrite); 159 return retVal; 160 } 161 162 /// ditto 163 alias insertAnywhere = insert; 164 165 /// ditto 166 alias put = insert; 167 168 /** 169 * Removes a single value from the tree, or does nothing. 170 * 171 * If `allowDuplicates` is true only a single element that is equivalent to 172 * the given `value` will be removed. Which of these elements is removed is 173 * not defined. 174 * 175 * Params: 176 * value = a value equal to the one to be removed 177 * cleanup = a function that should be run on the removed item 178 * Retuns: true if any value was removed 179 */ 180 bool remove(T value, void delegate(T) cleanup = null) 181 { 182 static if (stateSize!Allocator > 0) 183 immutable bool removed = root !is null && root.remove(cast(Value) value, root, allocator, cleanup); 184 else 185 immutable bool removed = root !is null && root.remove(cast(Value) value, root, null, cleanup); 186 if (removed) 187 { 188 --_length; 189 if (_length == 0) 190 { 191 static if (stateSize!Allocator > 0) 192 deallocateNode(root, allocator); 193 else 194 deallocateNode(root, null); 195 } 196 } 197 return removed; 198 } 199 200 /** 201 * Returns: true if the tree _conains the given value 202 */ 203 bool contains(T value) const @nogc @safe 204 { 205 return root !is null && root.contains(value); 206 } 207 208 /** 209 * Returns: the number of elements in the tree. 210 */ 211 size_t length() const pure nothrow @nogc @safe @property 212 { 213 return _length; 214 } 215 216 /** 217 * Returns: true if the tree is empty. 218 */ 219 bool empty() const pure nothrow @nogc @safe @property 220 { 221 return _length == 0; 222 } 223 224 /** 225 * Returns: a range over the tree. Do not insert into the tree while 226 * iterating because you may iterate over the same value multiple times 227 * or skip some values entirely. 228 */ 229 auto opSlice(this This)() inout @trusted @nogc 230 { 231 return Range!(This)(cast(const(Node)*) root, RangeType.all, T.init); 232 } 233 234 /** 235 * Returns: a range of elements which are less than value. 236 */ 237 auto lowerBound(this This)(inout T value) inout @trusted 238 { 239 return Range!(This)(cast(const(Node)*) root, RangeType.lower, value); 240 } 241 242 /** 243 * Returns: a range of elements which are equivalent (though not necessarily 244 * equal) to value. 245 */ 246 auto equalRange(this This)(inout T value) inout @trusted 247 { 248 return Range!(This)(cast(const(Node)*) root, RangeType.equal, value); 249 } 250 251 /** 252 * Returns: a range of elements which are greater than value. 253 */ 254 auto upperBound(this This)(inout T value) inout @trusted 255 { 256 return Range!(This)(cast(const(Node)*) root, RangeType.upper, value); 257 } 258 259 /** 260 * Returns: the first element in the tree. 261 */ 262 auto front(this This)() inout pure @trusted @property 263 { 264 import std.exception : enforce; 265 266 alias CET = ContainerElementType!(This, T); 267 enforce(!empty(), "Attepted to get the front of an empty tree."); 268 Node* current = cast(Node*) root; 269 while (current.left !is null) 270 current = current.left; 271 return cast(CET) current.values[0]; 272 } 273 274 /** 275 * Returns: the last element in the tree. 276 */ 277 auto back(this This)() inout pure @trusted @property 278 { 279 import std.exception : enforce; 280 281 alias CET = ContainerElementType!(This, T); 282 enforce(!empty(), "Attepted to get the back of an empty tree."); 283 Node* current = cast(Node*) root; 284 while (current.right !is null) 285 current = current.right; 286 return cast(CET) current.values[current.nextAvailableIndex - 1]; 287 } 288 289 /** 290 * Tree range 291 */ 292 static struct Range(ThisT) 293 { 294 @disable this(); 295 296 /** 297 * Standard range operations 298 */ 299 ET front() const @property @nogc 300 { 301 return cast(typeof(return)) current.values[index]; 302 } 303 304 /// ditto 305 bool empty() const pure nothrow @nogc @safe @property 306 { 307 return current is null; 308 } 309 310 /// ditto 311 void popFront() 312 { 313 _popFront(); 314 if (current is null) 315 return; 316 with (RangeType) final switch (type) 317 { 318 case upper: 319 case all: break; 320 case equal: 321 if (_less(val, front())) 322 current = null; 323 break; 324 case lower: 325 if (!_less(front(), val)) 326 current = null; 327 break; 328 } 329 } 330 331 package(containers): 332 333 // The TreeMap container needs to be able to modify part of the tree 334 // in-place. The reason that this works is that the value part of the 335 // key-value struct contained in a TTree used by a TreeMap is not used 336 // when comparing nodes. Normal users of the containers library cannot 337 // get a reference to the elements because modifying them will violate 338 // the ordering invariant of the tree. 339 T* _containersFront() const @property @nogc @trusted 340 { 341 return cast(T*) ¤t.values[index]; 342 } 343 344 private: 345 346 alias ET = ContainerElementType!(ThisT, T); 347 348 void currentToLeftmost() @nogc 349 { 350 if (current is null) 351 return; 352 while (current.left !is null) 353 current = current.left; 354 } 355 356 void currentToLeastContaining(inout T val) 357 { 358 if (current is null) 359 return; 360 while (current !is null) 361 { 362 assert(current.registry != 0, "Empty node"); 363 auto first = current.values[0]; 364 auto last = current.values[current.nextAvailableIndex - 1]; 365 immutable bool valLessFirst = _less(val, first); 366 immutable bool valLessLast = _less(val, last); 367 immutable bool firstLessVal = _less(first, val); 368 immutable bool lastLessVal = _less(last, val); 369 if (firstLessVal && valLessLast) 370 return; 371 else if (valLessFirst) 372 current = current.left; 373 else if (lastLessVal) 374 current = current.right; 375 else 376 { 377 static if (allowDuplicates) 378 { 379 if (!valLessFirst && !firstLessVal) 380 { 381 auto c = current; 382 current = current.left; 383 currentToLeastContaining(val); 384 if (current is null) 385 current = c; 386 return; 387 } 388 else 389 return; 390 } 391 else 392 return; 393 } 394 395 } 396 } 397 398 this(inout(Node)* n, RangeType type, inout T val) @nogc 399 { 400 current = n; 401 this.type = type; 402 this.val = val; 403 with (RangeType) final switch(type) 404 { 405 case all: 406 currentToLeftmost(); 407 break; 408 case lower: 409 currentToLeftmost(); 410 if (_less(val, front())) 411 current = null; 412 break; 413 case equal: 414 currentToLeastContaining(val); 415 while (current !is null && _less(front(), val)) 416 _popFront(); 417 if (current is null || _less(front(), val) || _less(val, front())) 418 current = null; 419 break; 420 case upper: 421 currentToLeastContaining(val); 422 while (current !is null && !_less(val, front())) 423 _popFront(); 424 break; 425 } 426 } 427 428 void _popFront() @nogc 429 in 430 { 431 assert (!empty, "Calling .popFront with empty TTree"); 432 } 433 do 434 { 435 index++; 436 if (index >= nodeCapacity || current.isFree(index)) 437 { 438 index = 0; 439 if (current.right !is null) 440 { 441 current = current.right; 442 while (current.left !is null) 443 current = current.left; 444 } 445 else if (current.parent is null) 446 current = null; 447 else if (current.parent.left is current) 448 current = current.parent; 449 else 450 { 451 while (current.parent.right is current) 452 { 453 current = current.parent; 454 if (current.parent is null) 455 { 456 current = null; 457 return; 458 } 459 } 460 current = current.parent; 461 } 462 } 463 } 464 465 size_t index; 466 const(Node)* current; 467 const RangeType type; 468 const T val; 469 } 470 471 mixin AllocatorState!Allocator; 472 473 /// The number of values that can be stored in a single T-Tree node. 474 enum size_t nodeCapacity = N[0]; 475 476 private: 477 478 import containers.internal.element_type : ContainerElementType; 479 import containers.internal.node : FatNodeInfo, fullBits, shouldAddGCRange, shouldNullSlot; 480 import containers.internal.storage_type : ContainerStorageType; 481 import std.algorithm : sort; 482 import std.functional: binaryFun; 483 import std.range : ElementType, isInputRange; 484 import std.traits: isPointer, PointerTarget; 485 import std.experimental.allocator.common : stateSize; 486 487 alias N = FatNodeInfo!(T.sizeof, 3, cacheLineSize, ulong.sizeof); 488 alias Value = ContainerStorageType!T; 489 alias BookkeepingType = N[1]; 490 enum HEIGHT_BIT_OFFSET = 48UL; 491 enum fullBitPattern = fullBits!(ulong, nodeCapacity); 492 enum RangeType : ubyte { all, lower, equal, upper } 493 enum bool useGC = supportGC && shouldAddGCRange!T; 494 495 static assert (nodeCapacity <= HEIGHT_BIT_OFFSET, "cannot fit height info and registry in ulong"); 496 static assert (nodeCapacity <= (typeof(Node.registry).sizeof * 8)); 497 static assert (Node.sizeof <= cacheLineSize); 498 499 // If we're storing a struct that defines opCmp, don't compare pointers as 500 // that is almost certainly not what the user intended. 501 static if (is(typeof(less) == string )) 502 { 503 // Everything inside of this `static if` is dumb. `binaryFun` does not 504 // correctly infer nothrow and @nogc attributes, among other things, so 505 // we need to declare a function here that has its attributes properly 506 // inferred. It's not currently possible, however, to use this function 507 // with std.algorithm.sort because of symbol visibility issues. Because 508 // of this problem, keep a duplicate of the sorting predicate in string 509 // form in the `_lessStr` alias. 510 static if (less == "a < b" && isPointer!T 511 && __traits(hasMember, PointerTarget!T, "opCmp")) 512 { 513 enum _lessStr = "a.opCmp(*b) < 0"; 514 static bool _less(TT)(const TT a, const TT b) 515 { 516 return a.opCmp(*b) < 0; 517 } 518 } 519 else 520 { 521 enum _lessStr = less; 522 alias _less = binaryFun!less; 523 } 524 } 525 else 526 alias _less = binaryFun!less; 527 528 static Node* allocateNode(Value value, Node* parent, AllocatorType allocator) @trusted 529 out (result) 530 { 531 assert (result.left is null); 532 assert (result.right is null); 533 } 534 do 535 { 536 import core.memory : GC; 537 import std.experimental.allocator : make; 538 539 static if (stateSize!Allocator == 0) 540 Node* n = make!Node(Allocator.instance); 541 else 542 Node* n = make!Node(allocator); 543 n.parent = parent; 544 n.markUsed(0); 545 n.values[0] = cast(Value) value; 546 static if (useGC) 547 GC.addRange(n, Node.sizeof); 548 return n; 549 } 550 551 static void deallocateNode(ref Node* n, AllocatorType allocator) 552 in 553 { 554 assert (n !is null); 555 } 556 do 557 { 558 import std.experimental.allocator : dispose; 559 import core.memory : GC; 560 561 if (n.left !is null) 562 deallocateNode(n.left, allocator); 563 if (n.right !is null) 564 deallocateNode(n.right, allocator); 565 566 static if (useGC) 567 GC.removeRange(n); 568 static if (stateSize!Allocator == 0) 569 dispose(Allocator.instance, n); 570 else 571 dispose(allocator, n); 572 n = null; 573 } 574 575 static struct Node 576 { 577 private size_t nextAvailableIndex() const pure nothrow @nogc @safe 578 { 579 import containers.internal.backwards : bsf; 580 581 return bsf(~(registry & fullBitPattern)); 582 } 583 584 private void markUsed(size_t index) pure nothrow @nogc @safe 585 { 586 registry |= (1UL << index); 587 } 588 589 private void markUnused(size_t index) pure nothrow @nogc @safe 590 { 591 registry &= ~(1UL << index); 592 static if (shouldNullSlot!T) 593 values[index] = null; 594 } 595 596 private bool isFree(size_t index) const pure nothrow @nogc @safe 597 { 598 return (registry & (1UL << index)) == 0; 599 } 600 601 private bool isFull() const pure nothrow @nogc @safe 602 { 603 return (registry & fullBitPattern) == fullBitPattern; 604 } 605 606 private bool isEmpty() const pure nothrow @nogc @safe 607 { 608 return (registry & fullBitPattern) == 0; 609 } 610 611 bool contains(Value value) const @trusted 612 { 613 import std.range : assumeSorted; 614 size_t i = nextAvailableIndex(); 615 if (_less(value, cast(Value) values[0])) 616 return left !is null && left.contains(value); 617 if (_less(values[i - 1], value)) 618 return right !is null && right.contains(value); 619 static if (is(typeof(_lessStr))) 620 return !assumeSorted!_lessStr(values[0 .. i]).equalRange(value).empty; 621 else 622 return !assumeSorted!_less(values[0 .. i]).equalRange(value).empty; 623 } 624 625 ulong calcHeight() pure nothrow @nogc @safe 626 { 627 immutable ulong l = left !is null ? left.height() : 0; 628 immutable ulong r = right !is null ? right.height() : 0; 629 immutable ulong h = 1 + (l > r ? l : r); 630 assert (h < ushort.max, "Height overflow"); 631 registry &= fullBitPattern; 632 registry |= (h << HEIGHT_BIT_OFFSET); 633 return h; 634 } 635 636 ulong height() const pure nothrow @nogc @safe 637 { 638 return registry >>> HEIGHT_BIT_OFFSET; 639 } 640 641 int imbalanced() const pure nothrow @nogc @safe 642 { 643 if (right !is null 644 && ((left is null && right.height() > 1) 645 || (left !is null && right.height() > left.height() + 1))) 646 return 1; 647 if (left !is null 648 && ((right is null && left.height() > 1) 649 || (right !is null && left.height() > right.height() + 1))) 650 return -1; 651 return 0; 652 } 653 654 bool insert(T value, ref Node* root, AllocatorType allocator, bool overwrite) @trusted 655 in 656 { 657 static if (isPointer!T || is (T == class) || is (T == interface)) 658 assert (value !is null, "Inserting null values is not allowed"); 659 } 660 do 661 { 662 import std.algorithm : sort; 663 import std.range : assumeSorted; 664 if (!isFull()) 665 { 666 immutable size_t index = nextAvailableIndex(); 667 static if (!allowDuplicates) 668 { 669 static if (is(typeof(_lessStr))) 670 auto r = assumeSorted!_lessStr(values[0 .. index]).trisect( 671 cast(Value) value); 672 else 673 auto r = assumeSorted!_less(values[0 .. index]).trisect( 674 cast(Value) value); 675 if (!r[1].empty) 676 { 677 if (overwrite) 678 { 679 values[r[0].length] = cast(Value) value; 680 return true; 681 } 682 return false; 683 } 684 } 685 values[index] = cast(Value) value; 686 markUsed(index); 687 static if (is(typeof(_lessStr))) 688 sort!_lessStr(values[0 .. index + 1]); 689 else 690 sort!_less(values[0 .. index + 1]); 691 return true; 692 } 693 if (_less(value, values[0])) 694 { 695 if (left is null) 696 { 697 left = allocateNode(cast(Value) value, &this, allocator); 698 calcHeight(); 699 return true; 700 } 701 immutable bool b = left.insert(value, root, allocator, overwrite); 702 if (imbalanced() == -1) 703 rotateRight(root, allocator); 704 calcHeight(); 705 return b; 706 } 707 if (_less(values[$ - 1], cast(Value) value)) 708 { 709 if (right is null) 710 { 711 right = allocateNode(value, &this, allocator); 712 calcHeight(); 713 return true; 714 } 715 immutable bool b = right.insert(value, root, allocator, overwrite); 716 if (imbalanced() == 1) 717 rotateLeft(root, allocator); 718 calcHeight(); 719 return b; 720 } 721 static if (!allowDuplicates) 722 { 723 static if (is(typeof(_lessStr))) 724 { 725 if (!assumeSorted!_lessStr(values[]).equalRange(cast(Value) value).empty) 726 return false; 727 } 728 else 729 { 730 if (!assumeSorted!_less(values[]).equalRange(cast(Value) value).empty) 731 return false; 732 } 733 } 734 735 Value[nodeCapacity + 1] temp = void; 736 temp[0 .. $ - 1] = values[]; 737 temp[$ - 1] = cast(Value) value; 738 static if (is(typeof(_lessStr))) 739 sort!_lessStr(temp[]); 740 else 741 sort!_less(temp[]); 742 if (right is null) 743 { 744 values[] = temp[0 .. $ - 1]; 745 right = allocateNode(temp[$ - 1], &this, allocator); 746 return true; 747 } 748 if (left is null) 749 { 750 values[] = temp[1 .. $]; 751 left = allocateNode(temp[0], &this, allocator); 752 return true; 753 } 754 if (right.height < left.height) 755 { 756 values[] = temp[0 .. $ - 1]; 757 immutable bool b = right.insert(temp[$ - 1], root, allocator, overwrite); 758 if (imbalanced() == 1) 759 rotateLeft(root, allocator); 760 calcHeight(); 761 return b; 762 } 763 values[] = temp[1 .. $]; 764 immutable bool b = left.insert(temp[0], root, allocator, overwrite); 765 if (imbalanced() == -1) 766 rotateRight(root, allocator); 767 calcHeight(); 768 return b; 769 } 770 771 bool remove(Value value, ref Node* n, AllocatorType allocator, 772 void delegate(T) cleanup = null) 773 { 774 import std.range : assumeSorted; 775 assert (!isEmpty(), "Calling .remove on an empty TTree.Node"); 776 if (isFull() && _less(value, values[0])) 777 { 778 immutable bool r = left !is null && left.remove(value, left, allocator, cleanup); 779 if (left.isEmpty()) 780 deallocateNode(left, allocator); 781 return r; 782 } 783 if (isFull() && _less(values[$ - 1], value)) 784 { 785 immutable bool r = right !is null && right.remove(value, right, allocator, cleanup); 786 if (right.isEmpty()) 787 deallocateNode(right, allocator); 788 return r; 789 } 790 size_t i = nextAvailableIndex(); 791 static if (is(typeof(_lessStr))) 792 auto sv = assumeSorted!_lessStr(values[0 .. i]); 793 else 794 auto sv = assumeSorted!_less(values[0 .. i]); 795 auto tri = sv.trisect(value); 796 if (tri[1].length == 0) 797 return false; 798 // Clean up removed item 799 if (cleanup !is null) 800 cleanup(tri[1][0]); 801 immutable size_t l = tri[0].length; 802 if (right is null && left is null) 803 { 804 Value[nodeCapacity - 1] temp; 805 temp[0 .. l] = values[0 .. l]; 806 temp[l .. $] = values[l + 1 .. $]; 807 values[0 .. $ - 1] = temp[]; 808 markUnused(nextAvailableIndex() - 1); 809 } 810 else if (right !is null) 811 { 812 Value[nodeCapacity - 1] temp; 813 temp[0 .. l] = values[0 .. l]; 814 temp[l .. $] = values[l + 1 .. $]; 815 values[0 .. $ - 1] = temp[]; 816 values[$ - 1] = right.removeSmallest(allocator); 817 if (right.isEmpty()) 818 deallocateNode(right, allocator); 819 } 820 else if (left !is null) 821 { 822 Value[nodeCapacity - 1] temp; 823 temp[0 .. l] = values[0 .. l]; 824 temp[l .. $] = values[l + 1 .. $]; 825 values[1 .. $] = temp[]; 826 values[0] = left.removeLargest(allocator); 827 if (left.isEmpty()) 828 deallocateNode(left, allocator); 829 } 830 return true; 831 } 832 833 Value removeSmallest(AllocatorType allocator) 834 in 835 { 836 assert (!isEmpty(), "Calling .removeSmallest on an empty TTree.Node"); 837 } 838 do 839 { 840 if (left is null && right is null) 841 { 842 Value r = values[0]; 843 Value[nodeCapacity - 1] temp = void; 844 temp[] = values[1 .. $]; 845 values[0 .. $ - 1] = temp[]; 846 markUnused(nextAvailableIndex() - 1); 847 return r; 848 } 849 if (left !is null) 850 { 851 auto r = left.removeSmallest(allocator); 852 if (left.isEmpty()) 853 deallocateNode(left, allocator); 854 return r; 855 } 856 Value r = values[0]; 857 Value[nodeCapacity - 1] temp = void; 858 temp[] = values[1 .. $]; 859 values[0 .. $ - 1] = temp[]; 860 values[$ - 1] = right.removeSmallest(allocator); 861 if (right.isEmpty()) 862 deallocateNode(right, allocator); 863 return r; 864 } 865 866 Value removeLargest(AllocatorType allocator) 867 in 868 { 869 assert (!isEmpty(), "Calling .removeLargest on an empty TTree.Node"); 870 } 871 out (result) 872 { 873 static if (isPointer!T || is (T == class) || is(T == interface)) 874 assert (result !is null, "Removed a null value"); 875 } 876 do 877 { 878 if (left is null && right is null) 879 { 880 immutable size_t i = nextAvailableIndex() - 1; 881 Value r = values[i]; 882 markUnused(i); 883 return r; 884 } 885 if (right !is null) 886 { 887 auto r = right.removeLargest(allocator); 888 if (right.isEmpty()) 889 deallocateNode(right, allocator); 890 return r; 891 } 892 Value r = values[$ - 1]; 893 Value[nodeCapacity - 1] temp = void; 894 temp[] = values[0 .. $ - 1]; 895 values[1 .. $] = temp[]; 896 values[0] = left.removeLargest(allocator); 897 if (left.isEmpty()) 898 deallocateNode(left, allocator); 899 return r; 900 } 901 902 void rotateLeft(ref Node* root, AllocatorType allocator) @safe 903 { 904 Node* newRoot; 905 if (right.left !is null && right.right is null) 906 { 907 newRoot = right.left; 908 newRoot.parent = this.parent; 909 newRoot.left = &this; 910 newRoot.left.parent = newRoot; 911 newRoot.right = right; 912 newRoot.right.parent = newRoot; 913 newRoot.right.left = null; 914 right = null; 915 left = null; 916 } 917 else 918 { 919 newRoot = right; 920 newRoot.parent = this.parent; 921 right = newRoot.left; 922 if (right !is null) 923 right.parent = &this; 924 newRoot.left = &this; 925 this.parent = newRoot; 926 } 927 cleanup(newRoot, root, allocator); 928 } 929 930 void rotateRight(ref Node* root, AllocatorType allocator) @safe 931 { 932 Node* newRoot; 933 if (left.right !is null && left.left is null) 934 { 935 newRoot = left.right; 936 newRoot.parent = this.parent; 937 newRoot.right = &this; 938 newRoot.right.parent = newRoot; 939 newRoot.left = left; 940 newRoot.left.parent = newRoot; 941 newRoot.left.right = null; 942 left = null; 943 right = null; 944 } 945 else 946 { 947 newRoot = left; 948 newRoot.parent = this.parent; 949 left = newRoot.right; 950 if (left !is null) 951 left.parent = &this; 952 newRoot.right = &this; 953 this.parent = newRoot; 954 } 955 cleanup(newRoot, root, allocator); 956 } 957 958 void cleanup(Node* newRoot, ref Node* root, AllocatorType allocator) @safe 959 { 960 if (newRoot.parent !is null) 961 { 962 if (newRoot.parent.right is &this) 963 newRoot.parent.right = newRoot; 964 else 965 newRoot.parent.left = newRoot; 966 } 967 else 968 root = newRoot; 969 newRoot.fillFromChildren(root, allocator); 970 if (newRoot.left !is null) 971 { 972 newRoot.left.fillFromChildren(root, allocator); 973 } 974 if (newRoot.right !is null) 975 { 976 newRoot.right.fillFromChildren(root, allocator); 977 } 978 if (newRoot.left !is null) 979 newRoot.left.calcHeight(); 980 if (newRoot.right !is null) 981 newRoot.right.calcHeight(); 982 newRoot.calcHeight(); 983 } 984 985 void fillFromChildren(ref Node* root, AllocatorType allocator) @trusted 986 { 987 while (!isFull()) 988 { 989 if (left !is null) 990 { 991 insert(left.removeLargest(allocator), root, allocator, false); 992 if (left.isEmpty()) 993 deallocateNode(left, allocator); 994 } 995 else if (right !is null) 996 { 997 insert(right.removeSmallest(allocator), root, allocator, false); 998 if (right.isEmpty()) 999 deallocateNode(right, allocator); 1000 } 1001 else 1002 return; 1003 } 1004 } 1005 1006 debug(EMSI_CONTAINERS) invariant() 1007 { 1008 import std.string : format; 1009 assert (&this !is null, "Null this"); 1010 assert (left !is &this, "%x, %x".format(left, &this)); 1011 assert (right !is &this, "%x, %x".format(right, &this)); 1012 if (left !is null) 1013 { 1014 assert (left.left !is &this, "%s".format(values)); 1015 assert (left.right !is &this, "%x, %x".format(left.right, &this)); 1016 assert (left.parent is &this, "%x, %x, %x".format(left, left.parent, &this)); 1017 } 1018 if (right !is null) 1019 { 1020 assert (right.left !is &this, "%s".format(values)); 1021 assert (right.right !is &this, "%s".format(values)); 1022 assert (right.parent is &this, "%x, %x, %x".format(right, right.parent, &this)); 1023 } 1024 } 1025 1026 Value[nodeCapacity] values; 1027 Node* left; 1028 Node* right; 1029 Node* parent; 1030 ulong registry = 1UL << HEIGHT_BIT_OFFSET; 1031 } 1032 1033 size_t _length; 1034 Node* root; 1035 } 1036 1037 version(emsi_containers_unittest) unittest 1038 { 1039 import core.memory : GC; 1040 import std.algorithm : equal, sort, map, filter, each; 1041 import std.array : array; 1042 import std.range : iota, walkLength, isInputRange; 1043 import std.string : format; 1044 import std.uuid : randomUUID; 1045 1046 { 1047 TTree!int kt; 1048 assert (kt.empty); 1049 foreach (i; 0 .. 200) 1050 assert (kt.insert(i)); 1051 assert(kt.front == 0); 1052 assert(kt.back == 199); 1053 assert(!kt.empty); 1054 assert(kt.length == 200); 1055 assert(kt.contains(30)); 1056 } 1057 1058 { 1059 TTree!int kt; 1060 assert (!kt.contains(5)); 1061 kt.insert(2_000); 1062 assert (kt.contains(2_000)); 1063 foreach_reverse (i; 0 .. 1_000) 1064 { 1065 assert (kt.insert(i)); 1066 } 1067 assert (!kt.contains(100_000)); 1068 } 1069 1070 { 1071 import std.random : uniform; 1072 TTree!int kt; 1073 foreach (i; 0 .. 1_000) 1074 kt.insert(uniform(0, 100_000)); 1075 } 1076 1077 { 1078 TTree!int kt; 1079 kt.insert(10); 1080 assert (kt.length == 1); 1081 assert (!kt.insert(10)); 1082 assert (kt.length == 1); 1083 } 1084 1085 { 1086 TTree!(int, Mallocator, true) kt; 1087 assert (kt.insert(1)); 1088 assert (kt.length == 1); 1089 assert (kt.insert(1)); 1090 assert (kt.length == 2); 1091 assert (kt.contains(1)); 1092 } 1093 1094 { 1095 TTree!(int) kt; 1096 foreach (i; 0 .. 200) 1097 assert (kt.insert(i)); 1098 assert (kt.length == 200); 1099 assert (kt.remove(79)); 1100 assert (!kt.remove(79)); 1101 assert (kt.length == 199); 1102 } 1103 1104 { 1105 string[] strs = [ 1106 "2c381d2a-bacd-40db-b6d8-055b144c5ee6", 1107 "62104b50-e235-4c95-bcb9-a545e88e2d09", 1108 "828c8fc0-a392-4738-a49c-62e991fce090", 1109 "62e30465-79eb-446e-b34f-af5d7c491486", 1110 "93ec245b-60d2-4422-91ff-66a6d7e299fc", 1111 "c1d2f3d7-82cc-4d90-a2c5-9fba335f36cd", 1112 "c9d8d980-94eb-4941-b873-00d68021522f", 1113 "82dbc4df-cb3c-447a-9d73-cd6291a0ba02", 1114 "8d259231-6ab6-49e4-9bb6-fe097c4153ed", 1115 "f9f2d719-61e1-4f62-ae2c-bf2a24a13d5b" 1116 ]; 1117 TTree!string strings; 1118 foreach (i, s; strs) 1119 assert (strings.insert(s)); 1120 sort(strs[]); 1121 assert (equal(strs, strings[])); 1122 } 1123 1124 foreach (x; 0 .. 1000) 1125 { 1126 TTree!string strings; 1127 string[] strs = iota(10).map!(a => randomUUID().toString()).array(); 1128 foreach (i, s; strs) 1129 assert (strings.insert(s)); 1130 assert (strings.length == strs.length); 1131 sort(strs); 1132 assert (equal(strs, strings[])); 1133 } 1134 1135 { 1136 TTree!string strings; 1137 strings.insert(["e", "f", "a", "b", "c", "d"]); 1138 assert (equal(strings[], ["a", "b", "c", "d", "e", "f"])); 1139 } 1140 1141 { 1142 TTree!(string, Mallocator, true) strings; 1143 assert (strings.insert("b")); 1144 assert (strings.insert("c")); 1145 assert (strings.insert("a")); 1146 assert (strings.insert("d")); 1147 assert (strings.insert("d")); 1148 assert (strings.length == 5); 1149 assert (equal(strings.equalRange("d"), ["d", "d"]), format("%s", strings.equalRange("d"))); 1150 assert (equal(strings.equalRange("a"), ["a"]), format("%s", strings.equalRange("a"))); 1151 assert (equal(strings.lowerBound("d"), ["a", "b", "c"]), format("%s", strings.lowerBound("d"))); 1152 assert (equal(strings.upperBound("c"), ["d", "d"]), format("%s", strings.upperBound("c"))); 1153 } 1154 1155 { 1156 static struct S 1157 { 1158 string x; 1159 int opCmp (ref const S other) const @nogc 1160 { 1161 if (x < other.x) 1162 return -1; 1163 if (x > other.x) 1164 return 1; 1165 return 0; 1166 } 1167 } 1168 1169 TTree!(S*, Mallocator, true) stringTree; 1170 auto one = S("offset"); 1171 stringTree.insert(&one); 1172 auto two = S("object"); 1173 assert (stringTree.equalRange(&two).empty); 1174 assert (!stringTree.equalRange(&one).empty); 1175 assert (stringTree[].front.x == "offset"); 1176 } 1177 1178 { 1179 static struct TestStruct 1180 { 1181 int opCmp(ref const TestStruct other) const @nogc 1182 { 1183 return x < other.x ? -1 : (x > other.x ? 1 : 0); 1184 } 1185 int x; 1186 int y; 1187 } 1188 TTree!(TestStruct*) tsTree; 1189 static assert (isInputRange!(typeof(tsTree[]))); 1190 foreach (i; 0 .. 100) 1191 assert(tsTree.insert(new TestStruct(i, i * 2))); 1192 assert (tsTree.length == 100); 1193 auto r = tsTree[]; 1194 TestStruct* prev = r.front(); 1195 r.popFront(); 1196 while (!r.empty) 1197 { 1198 assert (r.front.x > prev.x, format("%s %s", prev.x, r.front.x)); 1199 prev = r.front; 1200 r.popFront(); 1201 } 1202 TestStruct a = TestStruct(30, 100); 1203 auto eqArray = array(tsTree.equalRange(&a)); 1204 assert (eqArray.length == 1, format("%d", eqArray.length)); 1205 } 1206 1207 { 1208 import std.algorithm : canFind; 1209 TTree!int ints; 1210 foreach (i; 0 .. 50) 1211 ints ~= i; 1212 assert (canFind(ints[], 20)); 1213 assert (walkLength(ints[]) == 50); 1214 assert (walkLength(filter!(a => (a & 1) == 0)(ints[])) == 25); 1215 } 1216 1217 { 1218 TTree!int ints; 1219 foreach (i; 0 .. 50) 1220 ints ~= i; 1221 ints.remove(0); 1222 assert (ints.length == 49); 1223 foreach (i; 1 .. 12) 1224 ints.remove(i); 1225 assert (ints.length == 49 - 11); 1226 } 1227 1228 { 1229 const(TTree!(const(int))) getInts() 1230 { 1231 TTree!(const(int)) t; 1232 t.insert(1); 1233 t.insert(2); 1234 t.insert(3); 1235 return t; 1236 } 1237 auto t = getInts(); 1238 static assert (is (typeof(t[].front) == const(int))); 1239 assert (equal(t[].filter!(a => a & 1), [1, 3])); 1240 } 1241 1242 1243 { 1244 static struct ABC 1245 { 1246 ulong a; 1247 ulong b; 1248 1249 int opCmp(ref const ABC other) const @nogc 1250 { 1251 if (this.a < other.a) 1252 return -1; 1253 if (this.a > other.a) 1254 return 1; 1255 return 0; 1256 } 1257 } 1258 1259 TTree!(ABC, Mallocator, true) tree; 1260 foreach (i; 0 .. 10) 1261 tree.insert(ABC(i)); 1262 tree.insert(ABC(15)); 1263 tree.insert(ABC(15)); 1264 tree.insert(ABC(15)); 1265 tree.insert(ABC(15)); 1266 foreach (i; 20 .. 30) 1267 tree.insert(ABC(i)); 1268 assert(tree.equalRange(ABC(15)).walkLength() == 4, 1269 format("Actual length = %d", tree.equalRange(ABC(15)).walkLength())); 1270 } 1271 1272 { 1273 TTree!int ints2; 1274 iota(0, 1_000_000).each!(a => ints2.insert(a)); 1275 assert(equal(iota(0, 1_000_000), ints2[])); 1276 assert(ints2.length == 1_000_000); 1277 foreach (i; 0 .. 1_000_000) 1278 assert(!ints2.equalRange(i).empty, format("Could not find %d", i)); 1279 } 1280 1281 { 1282 TTree!int ints3; 1283 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0)) 1284 ints3.insert(i); 1285 assert(ints3.length == 500_000); 1286 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0)) 1287 assert(!ints3.equalRange(i).empty); 1288 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 1)) 1289 assert(ints3.equalRange(i).empty); 1290 } 1291 1292 { 1293 TTree!(ubyte, Mallocator, true) ubytes; 1294 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0).map!(a => cast(ubyte)(a % ubyte.max))) 1295 ubytes.insert(i); 1296 assert(ubytes[].walkLength == 500_000, "%d".format(ubytes[].walkLength)); 1297 assert(ubytes.length == 500_000, "%d".format(ubytes.length)); 1298 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0).map!(a => cast(ubyte)(a % ubyte.max))) 1299 assert(!ubytes.equalRange(i).empty); 1300 } 1301 1302 { 1303 import std.experimental.allocator.building_blocks.free_list : FreeList; 1304 import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; 1305 import std.experimental.allocator.building_blocks.region : Region; 1306 import std.experimental.allocator.building_blocks.stats_collector : StatsCollector; 1307 import std.stdio : stdout; 1308 1309 StatsCollector!(FreeList!(AllocatorList!(a => Region!(Mallocator)(1024 * 1024)), 1310 64)) allocator; 1311 { 1312 auto ints4 = TTree!(int, typeof(&allocator))(&allocator); 1313 foreach (i; 0 .. 10_000) 1314 ints4.insert(i); 1315 assert(walkLength(ints4[]) == 10_000); 1316 } 1317 assert(allocator.numAllocate == allocator.numDeallocate); 1318 assert(allocator.bytesUsed == 0); 1319 } 1320 } 1321 1322 version(emsi_containers_unittest) unittest 1323 { 1324 static class Foo 1325 { 1326 string name; 1327 1328 this(string s) 1329 { 1330 this.name = s; 1331 } 1332 } 1333 1334 TTree!(Foo, Mallocator, false, "a.name < b.name") tt; 1335 auto f = new Foo("foo"); 1336 tt.insert(f); 1337 f = new Foo("bar"); 1338 tt.insert(f); 1339 auto r = tt[]; 1340 } 1341 1342 version(emsi_containers_unittest) unittest 1343 { 1344 import std.range : walkLength; 1345 import std.stdio; 1346 1347 TTree!(int, Mallocator, true) tt; 1348 tt.insert(10); 1349 tt.insert(11); 1350 tt.insert(12); 1351 assert(tt.length == 3); 1352 tt.insert(11); 1353 assert(tt.length == 4); 1354 tt.remove(11); 1355 assert(tt.length == 3); 1356 assert(tt[].walkLength == tt.length); 1357 }