1 /** Tries and Prefix Trees. 2 3 Implementation is layered: 4 $(UL 5 $(LI `RawRadixTree` stores its untyped keys as variable length ubyte-strings (`UKey`)) 6 $(LI On top of that `RadixTree` implements typed-key access via its template parameter `Key`.) 7 ) 8 Both layers currently support 9 $(UL 10 $(LI template parameterization on the `Value`-type in the map case (when `Value` is non-`void`)) 11 $(LI `@nogc` and, when possible, `@safe pure nothrow`) 12 $(LI insertion with returned modifications status: `auto newKeyWasInserted = set.insert(Key key)`) 13 $(LI AA-style `in`-operator) 14 $(UL 15 $(LI `key in set` is `bool` for set-case) 16 $(LI `key in map` returns non-`null` `value` pointer when `key` is stored in `map`) 17 ) 18 $(LI AA-style iteration of map keys: `map.byKey()`) 19 $(LI AA-style iteration of map values: `map.byValue()`) 20 $(LI `SortedRange`-style iteration over elements sorted by key: `assert(set[].isSorted)`) 21 $(LI containment checking: `bool contains(in Key key)`) 22 $(LI element indexing and element index assignment for map case via `opIndex` and `opIndexAssign`) 23 $(LI key-prefix Completion (returning a `Range` over all set/map-elements that match a key prefix): `prefix(Key keyPrefix)`) 24 ) 25 Typed layer supports 26 $(UL 27 $(LI ordered access of aggregate types) 28 ) 29 30 See_Also: $(HTTP en.wikipedia.org/wiki/Trie) 31 See_Also: $(HTTP en.wikipedia.org/wiki/Radix_tree) 32 See_Also: $(HTTP github.com/nordlow/phobos-next/blob/master/src/test_trie_prefix.d) for a descriptive usage of prefixed access. 33 34 TODO split up file into raw_trie.d, trie.d 35 36 TODO use fast bit-scanning functions in core.bitop for DenseBranch and 37 DenseLeaf iterators 38 39 TODO shift addresses of class and pointers by its alignment before adding 40 them to make top-branch as dense possible 41 42 TODO Allow slicing from non-mutable tries and add test-case at line 5300 43 44 TODO 2. Make as many members as possible free functionss free-functions to 45 reduce compilation times. Alternative make them templates (`template-lazy` ) 46 47 TODO Use scope on `Range`, `RawRange` and members that return key and value 48 reference when DIP-1000 has been implemented 49 50 TODO Encode `string` with zero-terminating 0 byte when it's not the last 51 member in a key aggregate 52 53 TODO 54 55 Replace 56 case undefined: return typeof(return).init; // terminate recursion 57 with 58 case undefined: return curr; 59 60 TODO: 61 TODO Make `Key` and Ix[]-array of `immutable Ix` like `string` 62 63 TODO Allow `Node`-constructors to take const and immutable prefixes and then 64 make `toRawKey` and `toTypedKey` accept return const-slices 65 66 TODO Remove @trusted from VLA (variable length array)-members of SparseBranch/SparseLeaf and make their callers @trusted instead. 67 68 TODO Assure that ~this() is run for argument `nt` in `freeNode`. Can we use `postblit()` for this? 69 70 TODO Search for "functionize this loop or reuse memmove" and use move() 71 72 TODO Add Branch-hint allocation flag and re-benchmark construction of `radixTreeSet` with 10000000 uints 73 74 TODO Add sortedness to `IxsN` and make `IxsN.contains()` use `binarySearch()`. Make use of `sortn`. 75 76 TODO Check for case when expanding to bit-branch instead of `SparseBranch` in all `expand()` overloads 77 78 TODO Make array indexing/slicing as @trusted and use .ptr[] instead of [] when things are stable. 79 80 TODO Add various extra packings in MixLeaf1to4: number of 81 - Ix (0,1,2,3,4,5,6): 3-bits 82 - Ix2 (0,1,2,3): 2-bits 83 - Ix3 (0,1,2): 2-bits 84 - Ix4 (0,1): 1-bit 85 Total bits 8-bits 86 Possible packings with 6 bytes 87 - 4_ 88 - 4_2 89 - 4_2 90 - 4_2_1 91 - 4_1 92 - 4_1_1 93 - 3_2 94 - 3_2_1 95 - 3_1 96 - 3_1_1 97 - 3_1_1_1 98 - 2_2_2 99 - 2_2 100 - 2_2_1 101 - 2_2_1_1 102 - 2 103 - 2_1 104 - 2_1_1 105 - 2_1_1_1 106 - 2_1_1_1_1 107 - 1 108 - 1_1 109 - 1_1_1 110 - 1_1_1_1 111 - 1_1_1_1_1 112 - 1_1_1_1_1_1 113 114 TODO Sorted Range Primitives over Keys 115 116 - Returns a range of elements which are equivalent (though not necessarily equal) to value. 117 auto equalRange(this This)(inout T value) 118 119 - Returns a range of elements which are greater than low and smaller than highValue. 120 auto bound(this This)(inout T lowValue, inout T highValue) 121 122 - Returns a range of elements which are less than value. 123 auto lowerBound(this This)(inout T value) 124 125 - Returns a range of elements which are greater than value. 126 auto upperBound(this This)(inout T value) 127 128 TODO opBinaryRight shall return `_rawTree.ElementRef` instead of `bool` 129 130 TODO Fix vla-allocations in constructVariableSizeNode according 131 C11-recommendations. For reference set commit 132 d2f1971dd570439da4198fa76603b53b072060f8 at 133 https://github.com/emacs-mirror/emacs.git 134 */ 135 module nxt.trie; 136 137 import std.algorithm.mutation : move; 138 import std.algorithm.comparison : min, max; 139 import std.traits : isSomeString, isArray, isPointer, Unqual; 140 import std.range.primitives : isInputRange, ElementType, hasLength; 141 142 import nxt.bijections : isIntegralBijectableType, bijectToUnsigned, bijectFromUnsigned; 143 import nxt.variant_ex : WordVariant; 144 import nxt.typecons_ex : IndexedBy; 145 import nxt.container_traits : mustAddGCRange; 146 import nxt.dynamic_array : Array = DynamicArray; 147 148 // version = enterSingleInfiniteMemoryLeakTest; 149 // version = benchmark; 150 // version = show; 151 // version = useMemoryErrorHandler; 152 // version = showBranchSizes; 153 // version = showAssertTags; 154 155 alias isFixedTrieableKeyType = isIntegralBijectableType; 156 157 /** Returns: `true` if `T` is a scalar trie key-type, `false` otherwise. */ 158 enum isScalarTrieableKeyType(T) = (isFixedTrieableKeyType!T || 159 (isInputRange!T && 160 isFixedTrieableKeyType!(ElementType!T))); 161 162 /** Returns: `true` if `T` is a type that can be stored as a key in a trie, ` false` otherwise. */ 163 template isTrieableKeyType(T) 164 { 165 static if (is(T == struct)) 166 { 167 import std.meta : allSatisfy; 168 enum isTrieableKeyType = allSatisfy!(isScalarTrieableKeyType, // recurse 169 typeof(T.tupleof)); 170 } 171 else static if (is(T == class)) 172 { 173 static assert("Class types cannot be stored in a radix tree because classes in D has reference semantics"); 174 } 175 else 176 { 177 enum isTrieableKeyType = isScalarTrieableKeyType!T; 178 } 179 } 180 181 version(useMemoryErrorHandler) unittest 182 { 183 import etc.linux.memoryerror : registerMemoryErrorHandler; 184 registerMemoryErrorHandler(); 185 dbg("registerMemoryErrorHandler done"); 186 } 187 188 @safe pure nothrow @nogc unittest 189 { version(showAssertTags) dbg(); 190 static assert(isTrieableKeyType!(const(char)[])); 191 192 struct S { int x, y, z; double w; bool b; } 193 static assert(isTrieableKeyType!(S)); 194 } 195 196 /** Binary power of radix, typically either 1, 2, 4 or 8. */ 197 private enum span = 8; 198 /** Branch-multiplicity. Also called order. */ 199 private enum radix = 2^^span; 200 201 static assert(span == 8, "Radix is currently limited to 8"); 202 static assert(size_t.sizeof == 8, "Currently requires a 64-bit CPU (size_t.sizeof == 8)"); 203 204 version(unittest) 205 { 206 version = useModulo; 207 } 208 version = useModulo; // TODO remove and activate only in version(unittest) 209 210 /** Radix Modulo Index 211 Restricted index type avoids range checking in array indexing below. 212 */ 213 version(useModulo) 214 { 215 import nxt.modulo : Mod, mod; // TODO remove these if radix is `256` 216 alias Ix = Mod!(radix, ubyte); 217 alias UIx = Mod!(radix, size_t); // `size_t` is faster than `uint` on Intel Haswell 218 219 /** Mutable RawTree Key. */ 220 alias Key(size_t span) = Mod!(2^^span)[]; // TODO use static_bitarray to more naturally support span != 8. 221 /** Immutable RawTree Key. */ 222 alias IKey(size_t span) = immutable(Mod!(2^^span))[]; // TODO use static_bitarray to more naturally support span != 8. 223 /** Fixed-Length RawTree Key. */ 224 alias KeyN(size_t span, size_t N) = Mod!(2^^span)[N]; 225 226 enum useModuloFlag = true; 227 } 228 else 229 { 230 alias Ix = ubyte; 231 alias UIx = size_t; 232 233 /** Mutable RawTree Key. */ 234 alias Key(size_t span) = ubyte[]; // TODO use static_bitarray to more naturally support span != 8. 235 /** Immutable RawTree Key. */ 236 alias IKey(size_t span) = immutable(ubyte)[]; // TODO use static_bitarray to more naturally support span != 8. 237 /** Fixed-Length RawTree Key. */ 238 alias KeyN(size_t span, size_t N) = ubyte[N]; 239 240 enum useModuloFlag = false; 241 } 242 243 import nxt.static_modarray : StaticModArray; 244 alias IxsN(uint capacity, 245 uint elementLength) = StaticModArray!(capacity, 246 elementLength, 247 8, 248 useModuloFlag); 249 250 alias UKey = Key!span; 251 bool empty(UKey ukey) @safe pure nothrow @nogc 252 { 253 return ukey.length == 0; 254 } 255 256 private template IxElt(Value) 257 { 258 static if (is(Value == void)) // set case 259 { 260 alias IxElt = UIx; 261 } 262 else // map case 263 { 264 struct IxElt { UIx ix; Value value; } 265 } 266 } 267 268 private static auto eltIx(Value)(inout IxElt!Value elt) 269 { 270 static if (is(Value == void)) // set case 271 { 272 return elt; 273 } 274 else // map case 275 { 276 return elt.ix; 277 } 278 } 279 280 private template Elt(Value) 281 { 282 static if (is(Value == void)) // set case 283 { 284 alias Elt = UKey; 285 } 286 else // map case 287 { 288 struct Elt { UKey key; Value value; } 289 } 290 } 291 292 private auto eltKey(Value)(inout Elt!Value elt) 293 { 294 static if (is(Value == void)) // set case 295 { 296 return elt; 297 } 298 else // map case 299 { 300 return elt.key; 301 } 302 } 303 304 private auto eltKeyDropExactly(Value)(Elt!Value elt, size_t n) 305 { 306 static if (is(Value == void)) // set case 307 { 308 return elt[n .. $]; 309 } 310 else // map case 311 { 312 return Elt!Value(elt.key[n .. $], elt.value); 313 } 314 } 315 316 /** Results of attempt at modification sub. */ 317 enum ModStatus:ubyte 318 { 319 maxCapacityReached, // no operation, max capacity reached 320 unchanged, // no operation, element already stored 321 added, inserted = added, // new element was added (inserted) 322 updated, modified = updated, // existing element was update/modified 323 } 324 325 /** Size of a CPU cache line in bytes. 326 * 327 * Container layouts should be adapted to make use of at least this many bytes 328 * in its nodes. 329 */ 330 private enum cacheLineSize = 64; 331 332 shared static this() 333 { 334 import core.cpuid : dataCaches; 335 assert(cacheLineSize == dataCaches()[0].lineSize, "Cache line is not 64 bytes"); 336 } 337 338 enum keySeparator = ','; 339 340 /** Single/1-Key Leaf with maximum key-length 7. */ 341 struct OneLeafMax7 342 { 343 @safe pure: 344 enum capacity = 7; 345 346 this(Ix[] key) nothrow @nogc 347 { 348 pragma(inline, true); 349 assert(key.length != 0); 350 this.key = key; 351 } 352 353 bool contains(UKey key) const nothrow @nogc 354 { 355 pragma(inline, true); 356 return this.key == key; 357 } 358 359 @property string toString() const 360 { 361 import std.string : format; 362 string s; 363 foreach (immutable i, immutable ix; key) 364 { 365 immutable first = i == 0; // first iteration 366 if (!first) { s ~= '_'; } 367 s ~= format("%.2X", ix); // in hexadecimal 368 } 369 return s; 370 } 371 372 IxsN!(capacity, 1) key; 373 } 374 375 /** Binary/2-Key Leaf with key-length 3. */ 376 struct TwoLeaf3 377 { 378 enum keyLength = 3; // fixed length key 379 enum capacity = 2; // maximum number of keys stored 380 381 @safe pure nothrow @nogc: 382 383 this(Keys...)(Keys keys) 384 if (Keys.length != 0 && 385 Keys.length <= capacity) 386 { 387 pragma(inline, true); 388 this.keys = keys; 389 } 390 391 inout(Ix)[] prefix() inout 392 { 393 // version(LDC) pragma(inline, true); 394 assert(!keys.empty); 395 final switch (keys.length) 396 { 397 case 1: 398 return keys.at!0[]; 399 case 2: 400 import std.algorithm.searching : commonPrefix; 401 return commonPrefix(keys.at!0[], keys.at!1[]); 402 } 403 } 404 405 bool contains(UKey key) const 406 { 407 version(LDC) pragma(inline, true); 408 assert(!keys.empty); 409 final switch (keys.length) 410 { 411 case 1: return keys[0] == key; 412 case 2: return keys[0] == key || keys[1] == key; 413 } 414 // return keys.contains(key); 415 } 416 417 IxsN!(capacity, keyLength) keys; // should never be empty 418 } 419 420 /** Ternary/3-Key Leaf with key-length 2. */ 421 struct TriLeaf2 422 { 423 enum keyLength = 2; // fixed length key 424 enum capacity = 3; // maximum number of keys stored 425 426 @safe pure nothrow @nogc: 427 428 this(Keys...)(Keys keys) 429 if (Keys.length != 0 && 430 Keys.length <= capacity) 431 { 432 pragma(inline, true); 433 this.keys = keys; 434 } 435 436 inout(Ix)[] prefix() inout 437 { 438 // version(LDC) pragma(inline, true); 439 assert(!keys.empty); 440 import std.algorithm.searching : commonPrefix; 441 final switch (keys.length) 442 { 443 case 1: 444 return keys.at!0[]; 445 case 2: 446 return commonPrefix(keys.at!0[], keys.at!1[]); 447 case 3: 448 return commonPrefix(keys.at!0[], 449 commonPrefix(keys.at!1[], 450 keys.at!2[])); // TODO make and reuse variadic commonPrefix 451 } 452 } 453 454 bool contains(UKey key) const 455 { 456 version(LDC) pragma(inline, true); 457 assert(!keys.empty); 458 final switch (keys.length) 459 { 460 case 1: return keys[0] == key; 461 case 2: return keys[0] == key || keys[1] == key; 462 case 3: return keys[0] == key || keys[1] == key || keys[2] == key; 463 } 464 // return keys.contains(key); 465 } 466 467 IxsN!(capacity, keyLength) keys; // should never be empty 468 } 469 470 /** Hepa/7-Key Leaf with key-length 1. */ 471 struct HeptLeaf1 472 { 473 enum keyLength = 1; 474 enum capacity = 7; // maximum number of elements 475 476 @safe pure nothrow @nogc: 477 478 this(Keys...)(Keys keys) 479 if (Keys.length != 0 && 480 Keys.length <= capacity) 481 { 482 pragma(inline, true); 483 // static foreach (const ix, const key; keys) 484 // { 485 // this.keys[ix] = cast(Ix)key; 486 // } 487 this.keys = keys; 488 } 489 490 bool contains(UIx key) const 491 { 492 pragma(inline, true); 493 assert(!keys.empty); 494 // NOTE this is not noticably faster 495 // final switch (keys.length) 496 // { 497 // case 1: return keys[0] == key; 498 // case 2: return keys[0] == key || keys[1] == key; 499 // case 3: return keys[0] == key || keys[1] == key || keys[2] == key; 500 // case 4: return keys[0] == key || keys[1] == key || keys[2] == key || keys[3] == key; 501 // case 5: return keys[0] == key || keys[1] == key || keys[2] == key || keys[3] == key || keys[4] == key; 502 // case 6: return keys[0] == key || keys[1] == key || keys[2] == key || keys[3] == key || keys[4] == key || keys[5] == key; 503 // case 7: return keys[0] == key || keys[1] == key || keys[2] == key || keys[3] == key || keys[4] == key || keys[5] == key || keys[6] == key; 504 // } 505 return keys.contains(key); 506 } 507 bool contains(UKey key) const 508 { 509 pragma(inline, true); 510 return (key.length == 1 && 511 keys.contains(UIx(key[0]))); 512 } 513 514 IxsN!(capacity, 1) keys; // should never be empty 515 } 516 517 /** Check if type `NodeType` is a variable-length aggregate (`struct`) type. */ 518 private template hasVariableSize(NodeType) 519 { 520 import std.traits: hasMember; 521 enum hasVariableSize = hasMember!(NodeType, "allocationSizeOfCapacity"); 522 } 523 524 /** Allocate (if pointer) and Construct a `Node`-type of value type `NodeType` 525 * using constructor arguments `args` of `Args`. 526 */ 527 auto constructFixedSizeNode(NodeType, Args...)(Args args) @trusted pure nothrow @nogc 528 if (!hasVariableSize!NodeType) 529 { 530 static if (isPointer!NodeType) 531 { 532 // debug ++_heapAllocBalance; 533 import core.lifetime : emplace; 534 return emplace(cast(NodeType)malloc((*NodeType.init).sizeof), args); 535 // TODO ensure alignment of node at least that of NodeType.alignof 536 } 537 else 538 { 539 return NodeType(args); 540 } 541 } 542 543 /** Allocate (via malloc) and emplace an instance of a variable-length aggregate (`struct`) type `NodeType`. 544 * 545 * Construction is done using `malloc` plus `emplace`. 546 */ 547 private NodeType* constructVariableSizeNode(NodeType, Args...)(size_t requiredCapacity, Args args) @trusted 548 if (is(NodeType == struct) && 549 hasVariableSize!NodeType) 550 { 551 import std.math : nextPow2; 552 import std.algorithm.comparison : clamp; 553 const paddedRequestedCapacity = (requiredCapacity == 1 ? 554 1 : 555 (nextPow2(requiredCapacity - 1).clamp(NodeType.minCapacity, 556 NodeType.maxCapacity))); 557 558 // import nxt.dbgio; 559 // dbg(NodeType.stringof, " paddedRequestedCapacity:", paddedRequestedCapacity, " requiredCapacity:", requiredCapacity); 560 561 // assert(paddedRequestedCapacity >= requiredCapacity); // TODO this fails for dmd but not for ldc 562 563 import nxt.qcmeman : malloc; 564 import core.lifetime : emplace; 565 return emplace(cast(typeof(return))malloc(NodeType.allocationSizeOfCapacity(paddedRequestedCapacity)), 566 paddedRequestedCapacity, args); 567 } 568 569 void freeNode(NodeType)(NodeType nt) @trusted pure nothrow @nogc 570 { 571 static if (isPointer!NodeType) 572 { 573 free(cast(void*)nt); // TODO Allocator.free 574 // debug --_heapAllocBalance; 575 } 576 } 577 578 /** Sparsely coded leaves with values of type `Value`. */ 579 static private struct SparseLeaf1(Value) 580 { 581 import nxt.searching_ex : containsStoreIndex; 582 import std.range : assumeSorted; 583 import std.algorithm.sorting : isSorted; 584 585 enum hasValue = !is(Value == void); 586 587 enum minCapacity = 0; // preferred minimum number of preallocated values 588 589 // preferred maximum number of preallocated values, if larger use a DenseLeaf1 instead 590 static if (hasValue) 591 { 592 enum maxCapacity = 128; 593 } 594 else 595 { 596 enum maxCapacity = 48; 597 } 598 599 version(useModulo) 600 { 601 alias Capacity = Mod!(maxCapacity + 1); 602 } 603 else 604 { 605 alias Capacity = ubyte; 606 } 607 alias Length = Capacity; 608 609 pure nothrow @nogc: 610 611 this(size_t capacity) 612 { 613 _capacity = cast(Capacity)capacity; 614 _length = 0; 615 } 616 617 /** Returns a an allocated duplicate of this. 618 * 619 * Shallowly duplicates the values in the map case. 620 */ 621 typeof(this)* dup() 622 { 623 static if (hasValue) 624 { 625 return constructVariableSizeNode!(typeof(this))(this._capacity, ixs, values); 626 } 627 else 628 { 629 return constructVariableSizeNode!(typeof(this))(this._capacity, ixs); 630 } 631 } 632 633 static if (hasValue) 634 { 635 static if (mustAddGCRange!Value) 636 { 637 import core.memory : GC; 638 } 639 640 /** Construct with capacity `capacity`. */ 641 this(size_t capacity, Ix[] ixs, Value[] values) 642 in 643 { 644 assert(ixs.length == values.length); 645 assert(capacity >= ixs.length); 646 assert(values.length <= maxCapacity); 647 assert(ixs.isSorted); 648 } 649 do 650 { 651 _capacity = capacity; 652 _length = ixs.length; 653 foreach (immutable i, immutable ix; ixs) { ixsSlots[i] = ix; } 654 foreach (immutable i, immutable value; values) { valuesSlots[i] = value; } 655 static if (mustAddGCRange!Value) 656 { 657 GC.addRange(valuesSlots.ptr, _capacity * Value.sizeof); 658 } 659 } 660 } 661 else 662 { 663 this(size_t capacity, Ix[] ixs) 664 in 665 { 666 assert(capacity >= ixs.length); 667 assert(ixs.length <= maxCapacity); 668 assert(ixs.isSorted); 669 } 670 do 671 { 672 _capacity = cast(Capacity)capacity; 673 _length = cast(Length)ixs.length; 674 foreach (immutable i, immutable ix; ixs) { ixsSlots[i] = ix; } 675 } 676 } 677 678 ~this() @nogc 679 { 680 pragma(inline, true); 681 deinit(); 682 } 683 684 private void deinit() @trusted 685 { 686 pragma(inline, true); 687 static if (hasValue && 688 mustAddGCRange!Value) 689 { 690 GC.removeRange(valuesSlots.ptr, _capacity * Value.sizeof); 691 } 692 } 693 694 auto makeRoom() 695 { 696 auto next = &this; 697 if (full) 698 { 699 if (length < maxCapacity) // if we can expand more 700 { 701 // make room 702 static if (hasValue) 703 { 704 next = constructVariableSizeNode!(typeof(this))(length + 1, ixsSlots, valuesSlots); 705 } 706 else 707 { 708 next = constructVariableSizeNode!(typeof(this))(length + 1, ixsSlots); 709 } 710 this.deinit(); free(&this); // clear `this`. TODO reuse existing helper function in Phobos? 711 } 712 else 713 { 714 return null; // indicate that max capacity has been reached 715 } 716 } 717 return next; 718 } 719 720 /** Insert `key`, possibly self-reallocating `this` (into return). */ 721 typeof(this)* reconstructingInsert(IxElt!Value elt, 722 out ModStatus modStatus, 723 out size_t index) @trusted 724 { 725 // get index 726 static if (hasValue) 727 { 728 immutable ix = elt.ix; 729 } 730 else 731 { 732 immutable ix = elt; 733 } 734 735 // handle existing element 736 if (ixs.assumeSorted.containsStoreIndex(ix, index)) 737 { 738 static if (hasValue) 739 { 740 modStatus = valuesSlots[index] != elt.value ? ModStatus.updated : ModStatus.unchanged; 741 valuesSlots[index] = elt.value; 742 } 743 else 744 { 745 modStatus = ModStatus.unchanged; 746 } 747 return &this; 748 } 749 750 // try making room for new element 751 auto next = makeRoom(); 752 if (next is null) 753 { 754 modStatus = ModStatus.maxCapacityReached; // TODO expand to `DenseLeaf1` 755 return &this; 756 } 757 758 // insert new element 759 next.insertAt(index, elt); 760 modStatus = ModStatus.added; 761 762 return next; 763 } 764 765 private void insertAt(size_t index, IxElt!Value elt) 766 { 767 assert(index <= _length); 768 769 foreach (immutable i; 0 .. _length - index) // TODO functionize this loop or reuse memmove: 770 { 771 immutable iD = _length - i; 772 immutable iS = iD - 1; 773 ixsSlots[iD] = ixsSlots[iS]; 774 } 775 776 static if (hasValue) 777 { 778 ixsSlots[index] = elt.ix; 779 valuesSlots[index] = elt.value; 780 } 781 else 782 { 783 ixsSlots[index] = cast(Ix)elt; 784 } 785 786 ++_length; 787 } 788 789 pragma(inline, true) Length length() const @safe @nogc { return _length; } 790 pragma(inline, true) Capacity capacity() const @safe @nogc { return _capacity; } 791 792 pragma(inline, true) bool empty() const @safe @nogc { return _length == 0; } 793 pragma(inline, true) bool full() const @safe @nogc { return _length == _capacity; } 794 795 /** Get all initialized keys. */ 796 pragma(inline, true) auto ixs() inout @trusted @nogc { return ixsSlots[0 .. _length]; } 797 798 static if (hasValue) 799 { 800 /** Get all intialized values. */ 801 auto values() inout @trusted @nogc 802 { 803 pragma(inline, true) 804 return valuesSlots[0 .. _length]; 805 } 806 807 void setValue(UIx ix, in Value value) @trusted 808 { 809 pragma(inline, true); 810 size_t index; 811 immutable hit = ixs.assumeSorted.containsStoreIndex(ix, index); 812 assert(hit); // assert hit for now 813 assert(index < length); 814 values[index] = value; 815 } 816 817 inout(Value*) contains(UIx key) inout @nogc 818 { 819 pragma(inline, true); 820 size_t index; 821 if (ixs.assumeSorted.containsStoreIndex(key, index)) 822 { 823 return &(values[index]); 824 } 825 else 826 { 827 return null; 828 } 829 } 830 } 831 else 832 { 833 bool contains(UIx key) const @nogc 834 { 835 pragma(inline, true); 836 return ixs.assumeSorted.contains(key); 837 } 838 } 839 840 /** Get all reserved keys. */ 841 private auto ixsSlots() inout @trusted @nogc 842 { 843 pragma(inline, true); 844 static if (hasValue) 845 { 846 return (cast(Ix*)(_values.ptr + _capacity))[0 .. _capacity]; 847 } 848 else 849 { 850 return _ixs.ptr[0 .. _capacity]; 851 } 852 } 853 static if (hasValue) 854 { 855 /** Get all reserved values. */ 856 private auto valuesSlots() inout @trusted @nogc 857 { 858 pragma(inline, true); 859 return _values.ptr[0 .. _capacity]; 860 } 861 } 862 863 /** Get allocation size (in bytes) needed to hold `length` number of 864 * elements (keys and optionally values). 865 */ 866 static size_t allocationSizeOfCapacity(size_t capacity) @safe pure nothrow @nogc 867 { 868 static if (hasValue) 869 { 870 return (this.sizeof + // base plus 871 Value.sizeof*capacity + // actual size of `_values` 872 Ix.sizeof*capacity); // actual size of `_ixs` 873 } 874 else 875 { 876 return (this.sizeof + // base plus 877 Ix.sizeof*capacity); // actual size of `_ixs` 878 } 879 } 880 881 /** Get allocated size (in bytes) of `this` including the variable-length part. 882 */ 883 size_t allocatedSize() const @safe pure nothrow @nogc 884 { 885 return allocationSizeOfCapacity(_capacity); 886 } 887 888 private: 889 Length _length; 890 const Capacity _capacity; 891 static if (hasValue) 892 { 893 Value[0] _values; 894 } 895 Ix[0] _ixs; 896 } 897 898 /** Densely coded leaves with values of type `Value`. */ 899 static private struct DenseLeaf1(Value) 900 { 901 import nxt.static_bitarray : StaticBitArray; 902 903 enum hasValue = !is(Value == void); 904 905 static if (hasValue) 906 { 907 enum hasGCScannedValues = !is(Value == bool) && mustAddGCRange!Value; 908 } 909 else 910 { 911 enum hasGCScannedValues = false; 912 } 913 914 static if (hasGCScannedValues) 915 { 916 import core.memory : GC; 917 } 918 919 enum capacity = radix; 920 921 @safe pure nothrow @nogc: 922 923 static if (hasValue) 924 { 925 this(Ix[] ixs, Value[] values) 926 { 927 assert(ixs.length <= capacity); 928 assert(ixs.length == values.length); 929 foreach (immutable i, immutable ix; ixs) 930 { 931 _ixBits[ix] = true; 932 _values[ix] = values[i]; 933 } 934 static if (hasGCScannedValues) 935 { 936 GC.addRange(_values.ptr, capacity * Value.size); 937 } 938 } 939 940 this(ref StaticBitArray!capacity ixBits, Value[] values) 941 { 942 assert(ixBits.length == values.length); 943 _ixBits = ixBits; 944 _values[] = values[]; // TODO commenting out does not affect unittests 945 static if (hasGCScannedValues) 946 { 947 GC.addRange(_values.ptr, capacity * Value.size); 948 } 949 } 950 } 951 else 952 { 953 this(Ix[] ixs) 954 { 955 assert(ixs.length <= capacity); 956 foreach (immutable ix; ixs) 957 { 958 _ixBits[ix] = true; 959 } 960 } 961 962 this(const ref StaticBitArray!capacity ixBits) 963 { 964 _ixBits = ixBits; 965 } 966 } 967 968 969 /** Returns a an allocated duplicate of this. 970 Shallowly duplicates the values in the map case. 971 */ 972 typeof(this)* dup() 973 { 974 pragma(inline, true); 975 static if (hasValue) 976 { 977 return constructFixedSizeNode!(typeof(this)*)(_ixBits, _values); 978 } 979 else 980 { 981 return constructFixedSizeNode!(typeof(this)*)(_ixBits); 982 } 983 } 984 985 ~this() @nogc 986 { 987 pragma(inline, true); 988 static if (hasGCScannedValues) 989 { 990 GC.removeRange(_values.ptr, capacity * Value.size); 991 } 992 } 993 994 pragma(inline, true) bool empty() const { return _ixBits.allZero; } 995 pragma(inline, true) bool full() const { return _ixBits.allOne; } 996 pragma(inline, true) size_t count() const { return _ixBits.countOnes; } 997 998 static if (hasValue) 999 { 1000 pragma(inline, true) inout(Value*) contains(UIx ix) inout 1001 { 1002 return _ixBits[ix] ? &(_values[ix]) : null; 1003 } 1004 } 1005 else 1006 { 1007 pragma(inline, true) bool contains(UIx ix) const { return _ixBits[ix]; } 1008 } 1009 1010 ModStatus insert(IxElt!Value elt) 1011 { 1012 pragma(inline, true); 1013 1014 ModStatus modStatus; 1015 1016 static if (hasValue) 1017 { 1018 immutable ix = elt.ix; 1019 } 1020 else 1021 { 1022 immutable ix = elt; 1023 } 1024 1025 if (contains(ix)) 1026 { 1027 static if (hasValue) 1028 { 1029 modStatus = _values[ix] != elt.value ? ModStatus.updated : ModStatus.unchanged; 1030 } 1031 else 1032 { 1033 modStatus = ModStatus.unchanged; 1034 } 1035 } 1036 else 1037 { 1038 _ixBits[ix] = true; 1039 modStatus = ModStatus.added; 1040 } 1041 1042 // set element 1043 static if (hasValue) 1044 { 1045 _values[ix] = elt.value; 1046 } 1047 1048 return modStatus; 1049 } 1050 1051 /** Try to find index to first set bit in `_ixBits` starting at bit index `ix` and put the result in `nextIx`. 1052 Returns: `true` upon find, `false` otherwise. 1053 TODO move to StaticBitArray 1054 */ 1055 bool tryFindSetBitIx(UIx ix, out UIx nextIx) const 1056 { 1057 pragma(inline, true); 1058 assert(!_ixBits.allZero); 1059 return _ixBits.canFindIndexOf(true, ix, nextIx); 1060 } 1061 1062 bool tryFindNextSetBitIx(UIx ix, out UIx nextIx) const 1063 { 1064 pragma(inline, true); 1065 immutable ix1 = cast(uint)(ix + 1); 1066 return ix1 != radix && tryFindSetBitIx(UIx(ix1), nextIx); 1067 } 1068 1069 static if (hasValue) 1070 { 1071 /** Set value at index `ix` to `value`. */ 1072 pragma(inline, true) void setValue(UIx ix, in Value value) { _values[ix] = value; } 1073 1074 pragma(inline, true) auto values() inout { return _values; } 1075 } 1076 1077 private: 1078 StaticBitArray!capacity _ixBits; // 32 bytes 1079 static if (hasValue) 1080 { 1081 // static if (is(Value == bool)) 1082 // { 1083 // StaticBitArray!capacity _values; // packed values 1084 // } 1085 // else 1086 // { 1087 // Value[capacity] _values; 1088 // } 1089 Value[capacity] _values; 1090 } 1091 } 1092 1093 /** Fixed-Length leaf Key-only Node. */ 1094 alias FixedKeyLeafN = WordVariant!(OneLeafMax7, 1095 TwoLeaf3, 1096 TriLeaf2); 1097 1098 /** Mutable leaf node of 1-Ix leaves. 1099 * 1100 * Used by branch-leaf. 1101 */ 1102 alias Leaf1(Value) = WordVariant!(HeptLeaf1, // TODO remove from case when Value is void 1103 SparseLeaf1!Value*, 1104 DenseLeaf1!Value*); 1105 1106 /** Returns: `true` if `key` is stored under `curr`, `false` otherwise. */ 1107 UIx firstIx(Value)(Leaf1!Value curr) 1108 { 1109 final switch (curr.typeIx) with (Leaf1!Value.Ix) 1110 { 1111 case undefined: assert(false); 1112 case ix_HeptLeaf1: 1113 case ix_SparseLeaf1Ptr: 1114 return 0; // always first 1115 case ix_DenseLeaf1Ptr: 1116 auto curr_ = curr.as!(DenseLeaf1!Value*); 1117 UIx nextIx; 1118 immutable bool hit = curr_.tryFindSetBitIx(0, nextIx); 1119 assert(hit); 1120 return nextIx; 1121 } 1122 } 1123 1124 /** Try to iterate leaf index `ix` to index, either sparse or dense and put result in `nextIx`. 1125 * 1126 * Returns: `true` if `nextIx` was set, `false` if no more indexes was available. 1127 */ 1128 pragma(inline) bool tryNextIx(Value)(Leaf1!Value curr, const UIx ix, out Ix nextIx) 1129 { 1130 final switch (curr.typeIx) with (Leaf1!Value.Ix) 1131 { 1132 case undefined: assert(false); 1133 case ix_HeptLeaf1: 1134 immutable curr_ = curr.as!(HeptLeaf1); 1135 if (ix + 1 == curr_.keys.length) 1136 { 1137 return false; 1138 } 1139 else 1140 { 1141 nextIx = Ix(ix + 1); 1142 return true; 1143 } 1144 case ix_SparseLeaf1Ptr: 1145 immutable curr_ = curr.as!(SparseLeaf1!Value*); 1146 if (ix + 1 == curr_.length) 1147 { 1148 return false; 1149 } 1150 else 1151 { 1152 nextIx = Ix(ix + 1); 1153 return true; 1154 } 1155 case ix_DenseLeaf1Ptr: 1156 return curr.as!(DenseLeaf1!Value*).tryFindNextSetBitIx(ix, nextIx); 1157 } 1158 } 1159 1160 static assert((DenseLeaf1!void).sizeof == 32); 1161 1162 /** Adaptive radix tree (ART) container storing untyped (raw) keys. 1163 1164 In set-case (`Value` is `void`) this container contains specific memory 1165 optimizations for representing a set pointers or integral types (of fixed 1166 length). 1167 1168 Radix-trees are suitable for storing ordered sets/maps with variable-length 1169 keys and provide completion of all its keys matching a given 1170 key-prefix. This enables, for instance, efficient storage and retrieval of 1171 large sets of long URLs with high probability of sharing a common prefix, 1172 typically a domain and path. 1173 1174 Branch packing of leaves is more efficient when `Key.sizeof` is fixed, that 1175 is, the member `hasFixedKeyLength` returns `true`. 1176 1177 For optimal performance, the individual bit-chunks should be arranged 1178 starting with most sparse chunks first. For integers this means most 1179 significant chunk (byte) first. This includes IEEE-compliant floating point 1180 numbers. 1181 1182 For a good introduction to adaptive radix trees (ART) see $(HTTP infosys.cs.uni-saarland.de/publications/ARCD15.pdf) 1183 1184 See_Also: $(HTTP www.ietf.org/rfc/rfc2616.txt, RFC2616) 1185 1186 See_Also: $(HTTP en.wikipedia.org/wiki/Trie) 1187 See_Also: $(HTTP en.wikipedia.org/wiki/Radix_tree) 1188 See_Also: $(HTTP github.com/npgall/concurrent-trees) 1189 See_Also: $(HTTP code.dogmap.org/kart/) 1190 See_Also: $(HTTP cr.yp.to/critbit.html) 1191 See_Also: $(HTTP gcc.gnu.org/onlinedocs/libstdc++/ext/pb_ds/trie_based_containers.html) 1192 See_Also: $(HTTP github.com/npgall/concurrent-trees) 1193 */ 1194 template RawRadixTree(Value = void) 1195 { 1196 import std.bitmanip : bitfields; 1197 import std.algorithm.iteration : filter; 1198 import std.meta : AliasSeq, staticMap; 1199 1200 import nxt.static_bitarray : StaticBitArray; 1201 1202 enum isValue = !is(Value == void); 1203 1204 version(useModulo) 1205 { 1206 alias SubCount = Mod!(radix + 1); 1207 } 1208 else 1209 { 1210 alias SubCount = uint; // needed because for inclusive range 0 .. 256 1211 } 1212 1213 struct IxSub { UIx ix; Node node; } 1214 1215 /** Sparse/Packed dynamically sized branch implemented as variable-length 1216 struct. 1217 */ 1218 static private struct SparseBranch 1219 { 1220 enum minCapacity = 0; // minimum number of preallocated sub-indexes and sub-nodes 1221 enum maxCapacity = 48; // maximum number of preallocated sub-indexes and sub-nodes 1222 enum prefixCapacity = 5; // 5, 13, 21, ... 1223 1224 version(useModulo) 1225 { 1226 alias Count = Mod!(maxCapacity + 1); 1227 } 1228 else 1229 { 1230 alias Count = ubyte; 1231 } 1232 1233 @safe pure nothrow: 1234 1235 this(size_t subCapacity) 1236 { 1237 version(LDC) pragma(inline, true); 1238 initialize(subCapacity); 1239 } 1240 1241 this(size_t subCapacity, const Ix[] prefix, Leaf1!Value leaf1) 1242 { 1243 version(LDC) pragma(inline, true); 1244 initialize(subCapacity); 1245 this.prefix = prefix; 1246 this.leaf1 = leaf1; 1247 } 1248 1249 this(size_t subCapacity, const Ix[] prefix) 1250 { 1251 version(LDC) pragma(inline, true); 1252 initialize(subCapacity); 1253 this.prefix = prefix; 1254 } 1255 1256 this(size_t subCapacity, Leaf1!Value leaf1) 1257 { 1258 version(LDC) pragma(inline, true); 1259 initialize(subCapacity); 1260 this.leaf1 = leaf1; 1261 } 1262 1263 this(size_t subCapacity, const Ix[] prefix, IxSub sub) 1264 { 1265 version(LDC) pragma(inline, true); 1266 1267 assert(subCapacity); 1268 1269 initialize(subCapacity); 1270 1271 this.prefix = prefix; 1272 this.subCount = 1; 1273 this.subIxSlots[0] = sub.ix; 1274 this.subNodeSlots[0] = sub.node; 1275 } 1276 1277 this(size_t subCapacity, typeof(this)* rhs) 1278 in 1279 { 1280 assert(subCapacity > rhs.subCapacity); 1281 assert(rhs); 1282 } 1283 do 1284 { 1285 version(LDC) pragma(inline, true); 1286 // these two must be in this order: 1287 // 1. 1288 move(rhs.leaf1, this.leaf1); 1289 debug rhs.leaf1 = typeof(rhs.leaf1).init; 1290 this.subCount = rhs.subCount; 1291 move(rhs.prefix, this.prefix); 1292 1293 // 2. 1294 initialize(subCapacity); 1295 1296 // copy variable length part. TODO optimize: 1297 this.subIxSlots[0 .. rhs.subCount] = rhs.subIxSlots[0 .. rhs.subCount]; 1298 this.subNodeSlots[0 .. rhs.subCount] = rhs.subNodeSlots[0 .. rhs.subCount]; 1299 1300 assert(this.subCapacity > rhs.subCapacity); 1301 } 1302 1303 private pragma(inline, true) void initialize(size_t subCapacity) 1304 { 1305 // assert(subCapacity != 4); 1306 this.subCapacity = cast(Count)subCapacity; 1307 debug // only zero-initialize in debug mode 1308 { 1309 // zero-initialize variable-length part 1310 subIxSlots[] = Ix.init; 1311 subNodeSlots[] = Node.init; 1312 } 1313 } 1314 1315 typeof(this)* dup() @nogc 1316 { 1317 auto copy = constructVariableSizeNode!(typeof(this))(this.subCapacity); 1318 copy.leaf1 = dupAt(this.leaf1); 1319 copy.prefix = this.prefix; 1320 copy.subCount = this.subCount; 1321 1322 copy.subIxSlots[0 .. subCount] = this.subIxSlots[0 .. subCount]; // copy initialized 1323 debug copy.subIxSlots[subCount .. $] = Ix.init; // zero rest in debug 1324 1325 foreach (immutable i; 0 .. subCount) 1326 { 1327 copy.subNodeSlots[i] = dupAt(subNodeSlots[i]); 1328 } 1329 return copy; 1330 } 1331 1332 pragma(inline, true) ~this() @nogc { deinit(); } 1333 1334 private pragma(inline, true) void deinit() @nogc { /* nothing for now */ } 1335 1336 /** Insert `sub`, possibly self-reallocating `this` (into return). 1337 */ 1338 typeof(this)* reconstructingInsert(IxSub sub, 1339 out ModStatus modStatus, 1340 out size_t index) @trusted 1341 { 1342 auto next = &this; 1343 1344 import nxt.searching_ex : containsStoreIndex; 1345 if (subIxs.containsStoreIndex(sub.ix, index)) 1346 { 1347 assert(subIxSlots[index] == sub.ix); // subIxSlots[index] = sub[0]; 1348 subNodeSlots[index] = sub.node; 1349 modStatus = ModStatus.updated; 1350 return next; 1351 } 1352 1353 if (full) 1354 { 1355 if (subCount < maxCapacity) // if we can expand more 1356 { 1357 next = constructVariableSizeNode!(typeof(this))(subCount + 1, &this); 1358 1359 this.deinit(); free(&this); // clear `this`. TODO reuse existing helper function in Phobos? 1360 } 1361 else 1362 { 1363 modStatus = ModStatus.maxCapacityReached; // TODO expand to `DenseBranch` 1364 return next; 1365 } 1366 } 1367 1368 next.insertAt(index, sub); 1369 modStatus = ModStatus.added; 1370 return next; 1371 } 1372 1373 private void insertAt(size_t index, IxSub sub) 1374 { 1375 assert(index <= subCount); 1376 foreach (immutable i; 0 .. subCount - index) // TODO functionize this loop or reuse memmove: 1377 { 1378 immutable iD = subCount - i; 1379 immutable iS = iD - 1; 1380 subIxSlots[iD] = subIxSlots[iS]; 1381 subNodeSlots[iD] = subNodeSlots[iS]; 1382 } 1383 subIxSlots[index] = cast(Ix)sub.ix; // set new element 1384 subNodeSlots[index] = sub.node; // set new element 1385 ++subCount; 1386 } 1387 1388 inout(Node) subNodeAt(UIx ix) inout 1389 { 1390 import nxt.searching_ex : binarySearch; // need this instead of `SortedRange.contains` because we need the index 1391 immutable hitIndex = subIxSlots[0 .. subCount].binarySearch(ix); // find index where insertion should be made 1392 return (hitIndex != typeof(hitIndex).max) ? subNodeSlots[hitIndex] : Node.init; 1393 } 1394 1395 pragma(inline, true) bool empty() const @nogc { return subCount == 0; } 1396 pragma(inline, true) bool full() const @nogc { return subCount == subCapacity; } 1397 1398 pragma(inline, true) auto subIxs() inout @nogc 1399 { 1400 import std.range : assumeSorted; 1401 return subIxSlots[0 .. subCount].assumeSorted; 1402 } 1403 1404 pragma(inline, true) auto subNodes() inout @nogc { return subNodeSlots[0 .. subCount]; } 1405 1406 pragma(inline, true) Node firstSubNode() @nogc 1407 { 1408 return subCount ? subNodeSlots[0] : typeof(return).init; 1409 } 1410 1411 /** Get all sub-`Ix` slots, both initialized and uninitialized. */ 1412 private auto subIxSlots() inout @trusted pure nothrow 1413 { 1414 return (cast(Ix*)(_subNodeSlots0.ptr + subCapacity))[0 .. subCapacity]; 1415 } 1416 /** Get all sub-`Node` slots, both initialized and uninitialized. */ 1417 private auto subNodeSlots() inout @trusted pure nothrow 1418 { 1419 return _subNodeSlots0.ptr[0 .. subCapacity]; 1420 } 1421 1422 /** Append statistics of tree under `this` into `stats`. */ 1423 void calculate(ref Stats stats) const 1424 { 1425 size_t count = 0; // number of non-zero sub-nodes 1426 foreach (immutable sub; subNodes) 1427 { 1428 ++count; 1429 sub.calculate!(Value)(stats); 1430 } 1431 assert(count <= radix); 1432 ++stats.popHist_SparseBranch[count]; // TODO type-safe indexing 1433 1434 stats.sparseBranchAllocatedSizeSum += allocatedSize; 1435 1436 if (leaf1) 1437 { 1438 leaf1.calculate!(Value)(stats); 1439 } 1440 } 1441 1442 /** Get allocation size (in bytes) needed to hold `length` number of 1443 sub-indexes and sub-nodes. */ 1444 static size_t allocationSizeOfCapacity(size_t capacity) @safe pure nothrow @nogc 1445 { 1446 return (this.sizeof + // base plus 1447 Node.sizeof*capacity + // actual size of `_subNodeSlots0` 1448 Ix.sizeof*capacity); // actual size of `_subIxSlots0` 1449 } 1450 1451 /** Get allocated size (in bytes) of `this` including the variable-length part. */ 1452 size_t allocatedSize() const @safe pure nothrow @nogc 1453 { 1454 return allocationSizeOfCapacity(subCapacity); 1455 } 1456 1457 private: 1458 1459 // members in order of decreasing `alignof`: 1460 Leaf1!Value leaf1; 1461 1462 IxsN!(prefixCapacity, 1) prefix; // prefix common to all `subNodes` (also called edge-label) 1463 Count subCount; 1464 Count subCapacity; 1465 static assert(prefix.sizeof + subCount.sizeof + subCapacity.sizeof == 8); // assert alignment 1466 1467 // variable-length part 1468 Node[0] _subNodeSlots0; 1469 Ix[0] _subIxSlots0; // needs to special alignment 1470 } 1471 1472 static assert(hasVariableSize!SparseBranch); 1473 static if (!isValue) 1474 { 1475 static assert(SparseBranch.sizeof == 16); 1476 } 1477 1478 /** Dense/Unpacked `radix`-branch with `radix` number of sub-nodes. */ 1479 static private struct DenseBranch 1480 { 1481 enum maxCapacity = 256; 1482 enum prefixCapacity = 15; // 7, 15, 23, ..., we can afford larger prefix here because DenseBranch is so large 1483 1484 @safe pure nothrow: 1485 1486 this(const Ix[] prefix) 1487 { 1488 this.prefix = prefix; 1489 } 1490 1491 this(const Ix[] prefix, IxSub sub) 1492 { 1493 this(prefix); 1494 this.subNodes[sub.ix] = sub.node; 1495 } 1496 1497 this(const Ix[] prefix, IxSub subA, IxSub subB) 1498 { 1499 assert(subA.ix != subB.ix); // disjunct indexes 1500 assert(subA.node != subB.node); // disjunct nodes 1501 1502 this.subNodes[subA.ix] = subA.node; 1503 this.subNodes[subB.ix] = subB.node; 1504 } 1505 1506 this(SparseBranch* rhs) 1507 { 1508 this.prefix = rhs.prefix; 1509 1510 // move leaf 1511 move(rhs.leaf1, this.leaf1); 1512 debug rhs.leaf1 = Leaf1!Value.init; // make reference unique, to be on the safe side 1513 1514 foreach (immutable i; 0 .. rhs.subCount) // each sub node. TODO use iota!(Mod!N) 1515 { 1516 immutable iN = (cast(ubyte)i).mod!(SparseBranch.maxCapacity); 1517 immutable subIx = UIx(rhs.subIxSlots[iN]); 1518 1519 this.subNodes[subIx] = rhs.subNodeSlots[iN]; 1520 debug rhs.subNodeSlots[iN] = null; // make reference unique, to be on the safe side 1521 } 1522 } 1523 1524 typeof(this)* dup() @trusted // TODO remove @trusted qualifier when .ptr problem has been fixed 1525 { 1526 auto copy = constructFixedSizeNode!(typeof(this)*); 1527 copy.leaf1 = dupAt(leaf1); 1528 copy.prefix = prefix; 1529 foreach (immutable i, subNode; subNodes) 1530 { 1531 copy.subNodes.ptr[i] = dupAt(subNode); // TODO remove .ptr access when I inout problem is solved 1532 } 1533 return copy; 1534 } 1535 1536 /** Number of non-null sub-Nodes. */ 1537 SubCount subCount() const 1538 { 1539 typeof(return) count = 0; // number of non-zero sub-nodes 1540 foreach (immutable subNode; subNodes) // TODO why can't we use std.algorithm.count here? 1541 { 1542 if (subNode) { ++count; } 1543 } 1544 assert(count <= radix); 1545 return count; 1546 } 1547 1548 bool findSubNodeAtIx(size_t currIx, out UIx nextIx) inout @nogc 1549 { 1550 import std.algorithm.searching : countUntil; 1551 immutable cnt = subNodes[currIx .. $].countUntil!(subNode => cast(bool)subNode); 1552 immutable bool hit = cnt >= 0; 1553 if (hit) 1554 { 1555 const nextIx_ = currIx + cnt; 1556 assert(nextIx_ <= Ix.max); 1557 nextIx = cast(Ix)nextIx_; 1558 } 1559 return hit; 1560 } 1561 1562 /** Append statistics of tree under `this` into `stats`. */ 1563 void calculate(ref Stats stats) const 1564 { 1565 size_t count = 0; // number of non-zero sub-nodes 1566 foreach (immutable subNode; subNodes) 1567 { 1568 if (subNode) 1569 { 1570 ++count; 1571 subNode.calculate!(Value)(stats); 1572 } 1573 } 1574 assert(count <= radix); 1575 ++stats.popHist_DenseBranch[count]; // TODO type-safe indexing 1576 if (leaf1) { leaf1.calculate!(Value)(stats); } 1577 } 1578 1579 private: 1580 // members in order of decreasing `alignof`: 1581 Leaf1!Value leaf1; 1582 IxsN!(prefixCapacity, 1) prefix; // prefix (edge-label) common to all `subNodes` 1583 IndexedBy!(Node[radix], UIx) subNodes; 1584 } 1585 1586 version(showBranchSizes) 1587 { 1588 pragma(msg, "SparseBranch.sizeof:", SparseBranch.sizeof, " SparseBranch.alignof:", SparseBranch.alignof); 1589 pragma(msg, "DenseBranch.sizeof:", DenseBranch.sizeof, " DenseBranch.alignof:", DenseBranch.alignof); 1590 1591 pragma(msg, "SparseBranch.prefix.sizeof:", SparseBranch.prefix.sizeof, " SparseBranch.prefix.alignof:", SparseBranch.prefix.alignof); 1592 pragma(msg, "DenseBranch.prefix.sizeof:", DenseBranch.prefix.sizeof, " DenseBranch.prefix.alignof:", DenseBranch.prefix.alignof); 1593 1594 // pragma(msg, "SparseBranch.subNodes.sizeof:", SparseBranch.subNodes.sizeof, " SparseBranch.subNodes.alignof:", SparseBranch.subNodes.alignof); 1595 // pragma(msg, "SparseBranch.subIxs.sizeof:", SparseBranch.subIxs.sizeof, " SparseBranch.subIxs.alignof:", SparseBranch.subIxs.alignof); 1596 } 1597 1598 // TODO make these run-time arguments at different key depths and map to statistics of typed-key 1599 alias DefaultBranch = SparseBranch; // either `SparseBranch`, `DenseBranch` 1600 1601 /** Mutable node. */ 1602 alias Node = WordVariant!(OneLeafMax7, 1603 TwoLeaf3, 1604 TriLeaf2, 1605 1606 HeptLeaf1, 1607 SparseLeaf1!Value*, 1608 DenseLeaf1!Value*, 1609 1610 SparseBranch*, 1611 DenseBranch*); 1612 1613 /** Mutable branch node. */ 1614 alias Branch = WordVariant!(SparseBranch*, 1615 DenseBranch*); 1616 1617 static assert(Node.typeBits <= IxsN!(7, 1).typeBits); 1618 static assert(Leaf1!Value.typeBits <= IxsN!(7, 1).typeBits); 1619 static assert(Branch.typeBits <= IxsN!(7, 1).typeBits); 1620 1621 /** Constant node. */ 1622 // TODO make work with indexNaming 1623 // import std.typecons : ConstOf; 1624 // alias ConstNodePtr = WordVariant!(staticMap!(ConstOf, Node)); 1625 1626 static assert(span <= 8*Ix.sizeof, "Need more precision in Ix"); 1627 1628 /** Element reference. */ 1629 private static struct ElementRef 1630 { 1631 Node node; 1632 UIx ix; // `Node`-specific counter, typically either a sparse or dense index either a sub-branch or a `UKey`-ending `Ix` 1633 ModStatus modStatus; 1634 1635 pragma(inline, true) 1636 @safe pure nothrow: 1637 1638 bool opCast(T : bool)() const @nogc 1639 { 1640 return cast(bool)node; 1641 } 1642 } 1643 1644 /** Branch Range (Iterator). */ 1645 private static struct BranchRange 1646 { 1647 @safe pure nothrow: 1648 1649 this(SparseBranch* branch) 1650 { 1651 this.branch = Branch(branch); 1652 1653 this._subsEmpty = branch.subCount == 0; 1654 this._subCounter = 0; // always zero 1655 1656 if (branch.leaf1) 1657 { 1658 this.leaf1Range = Leaf1Range(branch.leaf1); 1659 } 1660 1661 cacheFront(); 1662 } 1663 1664 this(DenseBranch* branch) 1665 { 1666 this.branch = Branch(branch); 1667 1668 this._subCounter = 0; // TODO needed? 1669 _subsEmpty = !branch.findSubNodeAtIx(0, this._subCounter); 1670 1671 if (branch.leaf1) 1672 { 1673 this.leaf1Range = Leaf1Range(branch.leaf1); 1674 } 1675 1676 cacheFront(); 1677 } 1678 1679 pragma(inline, true) UIx frontIx() const @nogc 1680 { 1681 assert(!empty); 1682 return _cachedFrontIx; 1683 } 1684 1685 pragma(inline, true) bool atLeaf1() const @nogc 1686 { 1687 assert(!empty); 1688 return _frontAtLeaf1; 1689 } 1690 1691 private UIx subFrontIx() const 1692 { 1693 final switch (branch.typeIx) with (Branch.Ix) 1694 { 1695 case undefined: assert(false); 1696 case ix_SparseBranchPtr: 1697 return UIx(branch.as!(SparseBranch*).subIxs[_subCounter]); 1698 case ix_DenseBranchPtr: 1699 return _subCounter; 1700 } 1701 } 1702 1703 private Node subFrontNode() const 1704 { 1705 final switch (branch.typeIx) with (Branch.Ix) 1706 { 1707 case undefined: assert(false); 1708 case ix_SparseBranchPtr: return branch.as!(SparseBranch*).subNodeSlots[_subCounter]; 1709 case ix_DenseBranchPtr: return branch.as!(DenseBranch*).subNodes[_subCounter]; 1710 } 1711 } 1712 1713 void appendFrontIxsToKey(ref Array!Ix key) const @nogc 1714 { 1715 final switch (branch.typeIx) with (Branch.Ix) 1716 { 1717 case undefined: assert(false); 1718 case ix_SparseBranchPtr: 1719 key ~= branch.as!(SparseBranch*).prefix[]; 1720 break; 1721 case ix_DenseBranchPtr: 1722 key ~= branch.as!(DenseBranch*).prefix[]; 1723 break; 1724 } 1725 key ~= cast(Ix)frontIx; // uses cached data so ok to not depend on branch type 1726 } 1727 1728 size_t prefixLength() const @nogc 1729 { 1730 final switch (branch.typeIx) with (Branch.Ix) 1731 { 1732 case undefined: assert(false); 1733 case ix_SparseBranchPtr: return branch.as!(SparseBranch*).prefix.length; 1734 case ix_DenseBranchPtr: return branch.as!(DenseBranch*).prefix.length; 1735 } 1736 } 1737 1738 pragma(inline, true) bool empty() const @nogc { return leaf1Range.empty && _subsEmpty; } 1739 pragma(inline, true) bool subsEmpty() const @nogc { return _subsEmpty; } 1740 1741 /** Try to iterated forward. 1742 Returns: `true` upon successful forward iteration, `false` otherwise (upon completion of iteration), 1743 */ 1744 void popFront() 1745 { 1746 assert(!empty); 1747 1748 // pop front element 1749 if (_subsEmpty) 1750 { 1751 leaf1Range.popFront(); 1752 } 1753 else if (leaf1Range.empty) 1754 { 1755 popBranchFront(); 1756 } 1757 else // both non-empty 1758 { 1759 if (leaf1Range.front <= subFrontIx) // `a` before `ab` 1760 { 1761 leaf1Range.popFront(); 1762 } 1763 else 1764 { 1765 popBranchFront(); 1766 } 1767 } 1768 1769 if (!empty) { cacheFront(); } 1770 } 1771 1772 /** Fill cached value and remember if we we next element is direct 1773 (_frontAtLeaf1 is true) or under a sub-branch (_frontAtLeaf1 is 1774 false). 1775 */ 1776 void cacheFront() 1777 { 1778 assert(!empty); 1779 if (_subsEmpty) // no more sub-nodes 1780 { 1781 _cachedFrontIx = leaf1Range.front; 1782 _frontAtLeaf1 = true; 1783 } 1784 else if (leaf1Range.empty) // no more in direct leaf node 1785 { 1786 _cachedFrontIx = subFrontIx; 1787 _frontAtLeaf1 = false; 1788 } 1789 else // both non-empty 1790 { 1791 immutable leaf1Front = leaf1Range.front; 1792 if (leaf1Front <= subFrontIx) // `a` before `ab` 1793 { 1794 _cachedFrontIx = leaf1Front; 1795 _frontAtLeaf1 = true; 1796 } 1797 else 1798 { 1799 _cachedFrontIx = subFrontIx; 1800 _frontAtLeaf1 = false; 1801 } 1802 } 1803 } 1804 1805 private void popBranchFront() 1806 { 1807 // TODO move all calls to Branch-specific members popFront() 1808 final switch (branch.typeIx) with (Branch.Ix) 1809 { 1810 case undefined: assert(false); 1811 case ix_SparseBranchPtr: 1812 auto branch_ = branch.as!(SparseBranch*); 1813 if (_subCounter + 1 == branch_.subCount) { _subsEmpty = true; } else { ++_subCounter; } 1814 break; 1815 case ix_DenseBranchPtr: 1816 auto branch_ = branch.as!(DenseBranch*); 1817 _subsEmpty = !branch_.findSubNodeAtIx(_subCounter + 1, this._subCounter); 1818 break; 1819 } 1820 } 1821 1822 private: 1823 Branch branch; // branch part of range 1824 Leaf1Range leaf1Range; // range of direct leaves 1825 1826 UIx _subCounter; // `Branch`-specific counter, typically either a sparse or dense index either a sub-branch or a `UKey`-ending `Ix` 1827 UIx _cachedFrontIx; 1828 1829 bool _frontAtLeaf1; // `true` iff front is currently at a leaf1 element 1830 bool _subsEmpty; // `true` iff no sub-nodes exists 1831 } 1832 1833 /** Leaf1 Range (Iterator). */ 1834 private static struct Leaf1Range 1835 { 1836 this(Leaf1!Value leaf1) 1837 { 1838 this.leaf1 = leaf1; 1839 bool empty; 1840 this._ix = firstIx(empty); 1841 if (empty) 1842 { 1843 this.leaf1 = null; 1844 } 1845 } 1846 1847 @safe pure nothrow: 1848 1849 /** Get first index in current subkey. */ 1850 UIx front() const 1851 { 1852 assert(!empty); 1853 final switch (leaf1.typeIx) with (Leaf1!Value.Ix) 1854 { 1855 case undefined: assert(false); 1856 case ix_HeptLeaf1: 1857 static if (isValue) 1858 { 1859 assert(false, "HeptLeaf1 cannot store a value"); 1860 } 1861 else 1862 { 1863 return UIx(leaf1.as!(HeptLeaf1).keys[_ix]); 1864 } 1865 case ix_SparseLeaf1Ptr: 1866 return UIx(leaf1.as!(SparseLeaf1!Value*).ixs[_ix]); 1867 case ix_DenseLeaf1Ptr: 1868 return _ix; 1869 } 1870 } 1871 1872 static if (isValue) 1873 { 1874 Value frontValue() const 1875 { 1876 assert(!empty); 1877 final switch (leaf1.typeIx) with (Leaf1!Value.Ix) 1878 { 1879 case undefined: assert(false); 1880 case ix_HeptLeaf1: assert(false, "HeptLeaf1 cannot store a value"); 1881 case ix_SparseLeaf1Ptr: 1882 return leaf1.as!(SparseLeaf1!Value*).values[_ix]; 1883 case ix_DenseLeaf1Ptr: 1884 return leaf1.as!(DenseLeaf1!Value*).values[_ix]; 1885 } 1886 } 1887 } 1888 1889 bool empty() const @nogc { return leaf1.isNull; } 1890 1891 /** Pop front element. 1892 */ 1893 void popFront() 1894 { 1895 assert(!empty); 1896 1897 // TODO move all calls to leaf1-specific members popFront() 1898 final switch (leaf1.typeIx) with (Leaf1!Value.Ix) 1899 { 1900 case undefined: assert(false); 1901 case ix_HeptLeaf1: 1902 static if (isValue) 1903 { 1904 assert(false, "HeptLeaf1 cannot store a value"); 1905 } 1906 else 1907 { 1908 immutable leaf_ = leaf1.as!(HeptLeaf1); 1909 if (_ix + 1 == leaf_.keys.length) { leaf1 = null; } else { ++_ix; } 1910 break; 1911 } 1912 case ix_SparseLeaf1Ptr: 1913 const leaf_ = leaf1.as!(SparseLeaf1!Value*); 1914 if (_ix + 1 == leaf_.length) { leaf1 = null; } else { ++_ix; } 1915 break; 1916 case ix_DenseLeaf1Ptr: 1917 const leaf_ = leaf1.as!(DenseLeaf1!Value*); 1918 if (!leaf_.tryFindNextSetBitIx(_ix, _ix)) 1919 { 1920 leaf1 = null; 1921 } 1922 break; 1923 } 1924 } 1925 1926 static if (isValue) 1927 { 1928 auto ref value() inout 1929 { 1930 final switch (leaf1.typeIx) with (Leaf1!Value.Ix) 1931 { 1932 case undefined: assert(false); 1933 case ix_HeptLeaf1: assert(false, "HeptLeaf1 cannot store a value"); 1934 case ix_SparseLeaf1Ptr: return leaf1.as!(SparseLeaf1!Value*).values[_ix]; 1935 case ix_DenseLeaf1Ptr: return leaf1.as!(DenseLeaf1!Value*).values[_ix]; 1936 } 1937 } 1938 } 1939 1940 private UIx firstIx(out bool empty) const 1941 { 1942 final switch (leaf1.typeIx) with (Leaf1!Value.Ix) 1943 { 1944 case undefined: assert(false); 1945 case ix_HeptLeaf1: 1946 static if (isValue) 1947 { 1948 assert(false, "HeptLeaf1 cannot store a value"); 1949 } 1950 else 1951 { 1952 return UIx(0); // always first 1953 } 1954 case ix_SparseLeaf1Ptr: 1955 auto leaf_ = leaf1.as!(SparseLeaf1!Value*); 1956 empty = leaf_.empty; 1957 return UIx(0); // always first 1958 case ix_DenseLeaf1Ptr: 1959 auto leaf_ = leaf1.as!(DenseLeaf1!Value*); 1960 UIx nextIx; 1961 immutable bool hit = leaf_.tryFindSetBitIx(UIx(0), nextIx); 1962 assert(hit); 1963 return nextIx; 1964 } 1965 } 1966 1967 private: 1968 Leaf1!Value leaf1; // TODO Use Leaf1!Value-WordVariant when it includes non-Value leaf1 types 1969 UIx _ix; // `Node`-specific counter, typically either a sparse or dense index either a sub-branch or a `UKey`-ending `Ix` 1970 } 1971 1972 /** Leaf Value Range (Iterator). */ 1973 private static struct LeafNRange 1974 { 1975 this(Node leaf) 1976 { 1977 this.leaf = leaf; 1978 bool emptied; 1979 this.ix = firstIx(emptied); 1980 if (emptied) { this.leaf = null; } 1981 } 1982 1983 @safe pure nothrow: 1984 1985 private UIx firstIx(out bool emptied) const 1986 { 1987 switch (leaf.typeIx) with (Node.Ix) 1988 { 1989 case undefined: assert(false); 1990 case ix_OneLeafMax7: 1991 case ix_TwoLeaf3: 1992 case ix_TriLeaf2: 1993 case ix_HeptLeaf1: 1994 return UIx(0); // always first 1995 case ix_SparseLeaf1Ptr: 1996 const leaf_ = leaf.as!(SparseLeaf1!Value*); 1997 emptied = leaf_.empty; 1998 return UIx(0); // always first 1999 case ix_DenseLeaf1Ptr: 2000 const leaf_ = leaf.as!(DenseLeaf1!Value*); 2001 UIx nextIx; 2002 immutable bool hit = leaf_.tryFindSetBitIx(UIx(0), nextIx); 2003 assert(hit); 2004 return nextIx; 2005 default: assert(false, "Unsupported Node type"); 2006 } 2007 } 2008 2009 /** Get current subkey. */ 2010 Ix[] frontIxs() 2011 { 2012 assert(!empty); 2013 switch (leaf.typeIx) with (Node.Ix) 2014 { 2015 case undefined: assert(false); 2016 case ix_OneLeafMax7: 2017 assert(ix == 0); 2018 return leaf.as!(OneLeafMax7).key; 2019 case ix_TwoLeaf3: 2020 return leaf.as!(TwoLeaf3).keys[ix][]; 2021 case ix_TriLeaf2: 2022 return leaf.as!(TriLeaf2).keys[ix][]; 2023 case ix_HeptLeaf1: 2024 return [leaf.as!(HeptLeaf1).keys[ix]]; 2025 case ix_SparseLeaf1Ptr: 2026 return [leaf.as!(SparseLeaf1!Value*).ixs[ix]]; 2027 case ix_DenseLeaf1Ptr: 2028 return [Ix(ix)]; 2029 default: assert(false, "Unsupported Node type"); 2030 } 2031 } 2032 2033 /** Get first index in current subkey. */ 2034 UIx frontIx() 2035 { 2036 assert(!empty); 2037 switch (leaf.typeIx) with (Node.Ix) 2038 { 2039 case undefined: assert(false); 2040 case ix_OneLeafMax7: 2041 assert(ix == 0); 2042 return UIx(leaf.as!(OneLeafMax7).key[0]); 2043 case ix_TwoLeaf3: 2044 return UIx(leaf.as!(TwoLeaf3).keys[ix][0]); 2045 case ix_TriLeaf2: 2046 return UIx(leaf.as!(TriLeaf2).keys[ix][0]); 2047 case ix_HeptLeaf1: 2048 return UIx(leaf.as!(HeptLeaf1).keys[ix]); 2049 case ix_SparseLeaf1Ptr: 2050 return UIx(leaf.as!(SparseLeaf1!Value*).ixs[ix]); 2051 case ix_DenseLeaf1Ptr: 2052 return ix; 2053 default: assert(false, "Unsupported Node type"); 2054 } 2055 } 2056 2057 void appendFrontIxsToKey(ref Array!Ix key) const @nogc 2058 { 2059 assert(!empty); 2060 switch (leaf.typeIx) with (Node.Ix) 2061 { 2062 case undefined: assert(false); 2063 case ix_OneLeafMax7: 2064 assert(ix == 0); 2065 key ~= leaf.as!(OneLeafMax7).key[]; 2066 break; 2067 case ix_TwoLeaf3: 2068 key ~= leaf.as!(TwoLeaf3).keys[ix][]; 2069 break; 2070 case ix_TriLeaf2: 2071 key ~= leaf.as!(TriLeaf2).keys[ix][]; 2072 break; 2073 case ix_HeptLeaf1: 2074 key ~= Ix(leaf.as!(HeptLeaf1).keys[ix]); 2075 break; 2076 case ix_SparseLeaf1Ptr: 2077 key ~= Ix(leaf.as!(SparseLeaf1!Value*).ixs[ix]); 2078 break; 2079 case ix_DenseLeaf1Ptr: 2080 key ~= cast(Ix)ix; 2081 break; 2082 default: assert(false, "Unsupported Node type"); 2083 } 2084 } 2085 2086 pragma(inline, true) bool empty() const @nogc { return !leaf; } 2087 2088 pragma(inline, true) void makeEmpty() @nogc { leaf = null; } 2089 2090 /** Pop front element. */ 2091 void popFront() 2092 { 2093 assert(!empty); 2094 // TODO move all calls to leaf-specific members popFront() 2095 switch (leaf.typeIx) with (Node.Ix) 2096 { 2097 case undefined: assert(false); 2098 case ix_OneLeafMax7: 2099 makeEmpty; // only one element so forward automatically completes 2100 break; 2101 case ix_TwoLeaf3: 2102 auto leaf_ = leaf.as!(TwoLeaf3); 2103 if (ix + 1 == leaf_.keys.length) { makeEmpty; } else { ++ix; } 2104 break; 2105 case ix_TriLeaf2: 2106 auto leaf_ = leaf.as!(TriLeaf2); 2107 if (ix + 1 == leaf_.keys.length) { makeEmpty; } else { ++ix; } 2108 break; 2109 case ix_HeptLeaf1: 2110 auto leaf_ = leaf.as!(HeptLeaf1); 2111 if (ix + 1 == leaf_.keys.length) { makeEmpty; } else { ++ix; } 2112 break; 2113 case ix_SparseLeaf1Ptr: 2114 auto leaf_ = leaf.as!(SparseLeaf1!Value*); 2115 if (ix + 1 == leaf_.length) { makeEmpty; } else { ++ix; } 2116 break; 2117 case ix_DenseLeaf1Ptr: 2118 auto leaf_ = leaf.as!(DenseLeaf1!Value*); 2119 immutable bool emptied = !leaf_.tryFindNextSetBitIx(ix, ix); 2120 if (emptied) { makeEmpty; } 2121 break; 2122 default: assert(false, "Unsupported Node type"); 2123 } 2124 } 2125 2126 static if (isValue) 2127 { 2128 auto ref value() inout 2129 { 2130 switch (leaf.typeIx) with (Node.Ix) 2131 { 2132 case ix_SparseLeaf1Ptr: return leaf.as!(SparseLeaf1!Value*).values[ix]; 2133 case ix_DenseLeaf1Ptr: return leaf.as!(DenseLeaf1!Value*).values[ix]; 2134 default: assert(false, "Incorrect Leaf-type"); 2135 } 2136 } 2137 } 2138 2139 private: 2140 Node leaf; // TODO Use Leaf-WordVariant when it includes non-Value leaf types 2141 UIx ix; // `Node`-specific counter, typically either a sparse or dense index either a sub-branch or a `UKey`-ending `Ix` 2142 } 2143 2144 /** Ordered Set of BranchRanges. */ 2145 private static struct BranchRanges 2146 { 2147 static if (isValue) 2148 { 2149 bool appendFrontIxsToKey(ref Array!Ix key, ref Value value) const @trusted 2150 { 2151 foreach (const ref branchRange; _bRanges) 2152 { 2153 branchRange.appendFrontIxsToKey(key); 2154 if (branchRange.atLeaf1) 2155 { 2156 value = branchRange.leaf1Range.frontValue; 2157 return true; // key and value are both complete 2158 } 2159 } 2160 return false; // only key is partially complete 2161 } 2162 } 2163 else 2164 { 2165 bool appendFrontIxsToKey(ref Array!Ix key) const @trusted 2166 { 2167 foreach (const ref branchRange; _bRanges) 2168 { 2169 branchRange.appendFrontIxsToKey(key); 2170 if (branchRange.atLeaf1) 2171 { 2172 return true; // key is complete 2173 } 2174 } 2175 return false; // key is not complete 2176 } 2177 } 2178 size_t get1DepthAt(size_t depth) const @trusted 2179 { 2180 foreach (immutable i, ref branchRange; _bRanges[depth .. $]) 2181 { 2182 if (branchRange.atLeaf1) { return depth + i; } 2183 } 2184 return typeof(_branch1Depth).max; 2185 } 2186 2187 private void updateLeaf1AtDepth(size_t depth) @trusted 2188 { 2189 if (_bRanges[depth].atLeaf1) 2190 { 2191 if (_branch1Depth == typeof(_branch1Depth).max) // if not yet defined 2192 { 2193 _branch1Depth = min(depth, _branch1Depth); 2194 } 2195 } 2196 assert(_branch1Depth == get1DepthAt(0)); 2197 } 2198 2199 size_t getNext1DepthAt() const 2200 { 2201 pragma(inline, true); 2202 return get1DepthAt(_branch1Depth + 1); 2203 } 2204 2205 bool hasBranch1Front() const 2206 { 2207 pragma(inline, true); 2208 return _branch1Depth != typeof(_branch1Depth).max; 2209 } 2210 2211 void popBranch1Front() 2212 { 2213 pragma(inline, true); 2214 // _branchesKeyPrefix.popBackN(_bRanges.back); 2215 _bRanges[_branch1Depth].popFront(); 2216 } 2217 2218 bool emptyBranch1() const 2219 { 2220 pragma(inline, true); 2221 return _bRanges[_branch1Depth].empty; 2222 } 2223 2224 bool atLeaf1() const 2225 { 2226 pragma(inline, true); 2227 return _bRanges[_branch1Depth].atLeaf1; 2228 } 2229 2230 void shrinkTo(size_t newLength) 2231 { 2232 pragma(inline, true); 2233 // turn emptyness exception into an assert like ranges do 2234 // size_t suffixLength = 0; 2235 // foreach (const ref branchRange; _bRanges[$ - newLength .. $]) // TODO reverse isearch 2236 // { 2237 // suffixLength += branchRange.prefixLength + 1; 2238 // } 2239 // _branchesKeyPrefix.popBackN(suffixLength); 2240 _bRanges.length = newLength; 2241 } 2242 2243 void push(ref BranchRange branchRange) 2244 { 2245 pragma(inline, true); 2246 // branchRange.appendFrontIxsToKey(_branchesKeyPrefix); 2247 _bRanges ~= branchRange; 2248 } 2249 2250 size_t branchCount() const @safe pure nothrow @nogc 2251 { 2252 pragma(inline, true); 2253 return _bRanges.length; 2254 } 2255 2256 void pop() 2257 { 2258 pragma(inline, true); 2259 // _branchesKeyPrefix.popBackN(_bRanges.back.prefixLength + 1); 2260 _bRanges.popBack(); 2261 } 2262 2263 ref BranchRange bottom() 2264 { 2265 pragma(inline, true); 2266 return _bRanges.back; 2267 } 2268 2269 private void verifyBranch1Depth() 2270 { 2271 pragma(inline, true); 2272 assert(_branch1Depth == get1DepthAt(0)); 2273 } 2274 2275 void resetBranch1Depth() 2276 { 2277 pragma(inline, true); 2278 _branch1Depth = typeof(_branch1Depth).max; 2279 } 2280 2281 @property typeof(this) save() 2282 { 2283 typeof(this) copy; 2284 copy._bRanges = this._bRanges.dup; // ...this is inferred as @safe 2285 copy._branch1Depth = this._branch1Depth; 2286 return copy; 2287 } 2288 2289 private: 2290 Array!BranchRange _bRanges; 2291 // Array!Ix _branchesKeyPrefix; 2292 2293 // index to first branchrange in `_bRanges` that is currently on a leaf1 2294 // or `typeof.max` if undefined 2295 size_t _branch1Depth = size_t.max; 2296 } 2297 2298 /** Forward (Left) Single-Directional Range over Tree. */ 2299 private static struct FrontRange 2300 { 2301 @safe pure nothrow @nogc: 2302 2303 this(Node root) 2304 { 2305 if (root) 2306 { 2307 diveAndVisitTreeUnder(root, 0); 2308 cacheFront(); 2309 } 2310 } 2311 2312 void popFront() 2313 { 2314 debug branchRanges.verifyBranch1Depth(); 2315 2316 if (branchRanges.hasBranch1Front) // if we're currently at leaf1 of branch 2317 { 2318 popFrontInBranchLeaf1(); 2319 } 2320 else // if bottommost leaf should be popped 2321 { 2322 leafNRange.popFront(); 2323 if (leafNRange.empty) 2324 { 2325 postPopTreeUpdate(); 2326 } 2327 } 2328 2329 if (!empty) { cacheFront(); } 2330 } 2331 2332 private void popFrontInBranchLeaf1() // TODO move to member of BranchRanges 2333 { 2334 branchRanges.popBranch1Front(); 2335 if (branchRanges.emptyBranch1) 2336 { 2337 branchRanges.shrinkTo(branchRanges._branch1Depth); // remove `branchRange` and all others below 2338 branchRanges.resetBranch1Depth(); 2339 postPopTreeUpdate(); 2340 } 2341 else if (!branchRanges.atLeaf1) // if not at leaf 2342 { 2343 branchRanges._branch1Depth = branchRanges.getNext1DepthAt; 2344 } 2345 else // still at leaf 2346 { 2347 // nothing needed 2348 } 2349 } 2350 2351 private void cacheFront() 2352 { 2353 _cachedFrontKey.length = 0; // not clear() because we want to keep existing allocation 2354 2355 // branches 2356 static if (isValue) 2357 { 2358 if (branchRanges.appendFrontIxsToKey(_cachedFrontKey, 2359 _cachedFrontValue)) { return; } 2360 } 2361 else 2362 { 2363 if (branchRanges.appendFrontIxsToKey(_cachedFrontKey)) { return; } 2364 } 2365 2366 // leaf 2367 if (!leafNRange.empty) 2368 { 2369 leafNRange.appendFrontIxsToKey(_cachedFrontKey); 2370 static if (isValue) 2371 { 2372 _cachedFrontValue = leafNRange.value; // last should be leaf containing value 2373 } 2374 } 2375 } 2376 2377 // Go upwards and iterate forward in parents. 2378 private void postPopTreeUpdate() 2379 { 2380 while (branchRanges.branchCount) 2381 { 2382 branchRanges.bottom.popFront(); 2383 if (branchRanges.bottom.empty) 2384 { 2385 branchRanges.pop(); 2386 } 2387 else // if not empty 2388 { 2389 if (branchRanges.bottom.atLeaf1) 2390 { 2391 branchRanges._branch1Depth = min(branchRanges.branchCount - 1, 2392 branchRanges._branch1Depth); 2393 } 2394 break; // branchRanges.bottom is not empty so break 2395 } 2396 } 2397 if (branchRanges.branchCount && 2398 !branchRanges.bottom.subsEmpty) // if any sub nodes 2399 { 2400 diveAndVisitTreeUnder(branchRanges.bottom.subFrontNode, 2401 branchRanges.branchCount); // visit them 2402 } 2403 } 2404 2405 /** Find ranges of branches and leaf for all nodes under tree `root`. */ 2406 private void diveAndVisitTreeUnder(Node root, size_t depth) 2407 { 2408 Node curr = root; 2409 Node next; 2410 do 2411 { 2412 final switch (curr.typeIx) with (Node.Ix) 2413 { 2414 case undefined: assert(false); 2415 case ix_OneLeafMax7: 2416 case ix_TwoLeaf3: 2417 case ix_TriLeaf2: 2418 case ix_HeptLeaf1: 2419 case ix_SparseLeaf1Ptr: 2420 case ix_DenseLeaf1Ptr: 2421 assert(leafNRange.empty); 2422 leafNRange = LeafNRange(curr); 2423 next = null; // we're done diving 2424 break; 2425 case ix_SparseBranchPtr: 2426 auto curr_ = curr.as!(SparseBranch*); 2427 auto currRange = BranchRange(curr_); 2428 branchRanges.push(currRange); 2429 branchRanges.updateLeaf1AtDepth(depth); 2430 next = (curr_.subCount) ? curr_.firstSubNode : Node.init; 2431 break; 2432 case ix_DenseBranchPtr: 2433 auto curr_ = curr.as!(DenseBranch*); 2434 auto currRange = BranchRange(curr_); 2435 branchRanges.push(currRange); 2436 branchRanges.updateLeaf1AtDepth(depth); 2437 next = branchRanges.bottom.subsEmpty ? Node.init : branchRanges.bottom.subFrontNode; 2438 break; 2439 } 2440 curr = next; 2441 ++depth; 2442 } 2443 while (next); 2444 } 2445 2446 @property typeof(this) save() @trusted // TODO remove @trusted 2447 { 2448 typeof(this) copy; 2449 copy.leafNRange = this.leafNRange; 2450 copy.branchRanges = this.branchRanges.save; 2451 copy._cachedFrontKey = this._cachedFrontKey.dup; 2452 static if (isValue) 2453 { 2454 copy._cachedFrontValue = this._cachedFrontValue; 2455 } 2456 return copy; 2457 } 2458 2459 /** Check if empty. */ 2460 bool empty() const 2461 { 2462 pragma(inline, true); 2463 return (leafNRange.empty && 2464 branchRanges.branchCount == 0); 2465 } 2466 2467 /** Get front key. */ 2468 auto frontKey() const @trusted 2469 { 2470 pragma(inline, true); 2471 return _cachedFrontKey[]; // TODO replace @trusted with DIP-1000 scope 2472 } 2473 2474 static if (isValue) 2475 { 2476 /** Get front value. */ 2477 auto frontValue() const 2478 { 2479 pragma(inline, true); 2480 return _cachedFrontValue; 2481 } 2482 } 2483 2484 private: 2485 LeafNRange leafNRange; 2486 BranchRanges branchRanges; 2487 2488 // cache 2489 Array!Ix _cachedFrontKey; // copy of front key 2490 static if (isValue) 2491 { 2492 Value _cachedFrontValue; // copy of front value 2493 } 2494 } 2495 2496 /** Bi-Directional Range over Tree. 2497 Fulfills `isBidirectionalRange`. 2498 */ 2499 private static struct Range 2500 { 2501 import nxt.array_algorithm : startsWith; 2502 2503 pure nothrow @nogc: 2504 2505 this(Node root, UKey keyPrefix) 2506 { 2507 this._rawKeyPrefix = keyPrefix; 2508 2509 this._front = FrontRange(root); 2510 // TODO this._back = FrontRange(root); 2511 2512 if (!empty && 2513 !_front.frontKey.startsWith(_rawKeyPrefix)) 2514 { 2515 popFront(); 2516 } 2517 } 2518 2519 @property typeof(this) save() 2520 { 2521 typeof(this) copy; 2522 copy._front = this._front.save; 2523 copy._back = this._back.save; 2524 copy._rawKeyPrefix = this._rawKeyPrefix; 2525 return copy; 2526 } 2527 2528 bool empty() const 2529 { 2530 pragma(inline, true); 2531 return _front.empty; // TODO _front == _back; 2532 } 2533 2534 auto lowKey() const 2535 { 2536 pragma(inline, true); 2537 return _front.frontKey[_rawKeyPrefix.length .. $]; 2538 } 2539 auto highKey() const 2540 { 2541 pragma(inline, true); 2542 return _back.frontKey[_rawKeyPrefix.length .. $]; 2543 } 2544 2545 void popFront() 2546 { 2547 assert(!empty); 2548 do { _front.popFront(); } 2549 while (!empty && 2550 !_front.frontKey.startsWith(_rawKeyPrefix)); 2551 } 2552 2553 void popBack() 2554 { 2555 assert(!empty); 2556 do { _back.popFront(); } 2557 while (!empty && 2558 !_back.frontKey.startsWith(_rawKeyPrefix)); 2559 } 2560 2561 private: 2562 FrontRange _front; 2563 FrontRange _back; 2564 UKey _rawKeyPrefix; 2565 } 2566 2567 /** Sparse-Branch population histogram. */ 2568 alias SparseLeaf1_PopHist = size_t[radix]; 2569 2570 /** 256-Branch population histogram. */ 2571 alias DenseLeaf1_PopHist = size_t[radix]; 2572 2573 /** 4-Branch population histogram. 2574 Index maps to population with value range (0 .. 4). 2575 */ 2576 alias SparseBranch_PopHist = size_t[SparseBranch.maxCapacity + 1]; 2577 2578 /** Radix-Branch population histogram. 2579 Index maps to population with value range (0 .. `radix`). 2580 */ 2581 alias DenseBranch_PopHist = size_t[radix + 1]; 2582 2583 /** Tree Population and Memory-Usage Statistics. */ 2584 struct Stats 2585 { 2586 SparseLeaf1_PopHist popHist_SparseLeaf1; 2587 DenseLeaf1_PopHist popHist_DenseLeaf1; 2588 SparseBranch_PopHist popHist_SparseBranch; // packed branch population histogram 2589 DenseBranch_PopHist popHist_DenseBranch; // full branch population histogram 2590 2591 import nxt.typecons_ex : IndexedArray; 2592 2593 /** Maps `Node` type/index `Ix` to population. 2594 2595 Used to calculate complete tree memory usage, excluding allocator 2596 overhead typically via `malloc` and `calloc`. 2597 */ 2598 IndexedArray!(size_t, Node.Ix) popByNodeType; 2599 static assert(is(typeof(popByNodeType).Index == Node.Ix)); 2600 2601 IndexedArray!(size_t, Leaf1!Value.Ix) popByLeaf1Type; 2602 static assert(is(typeof(popByLeaf1Type).Index == Leaf1!Value.Ix)); 2603 2604 /** Number of heap-allocated `Node`s. Should always equal `heapAllocationBalance`. */ 2605 size_t heapNodeCount; 2606 2607 /** Number of heap-allocated `Leaf`s. Should always equal `heapLeafAllocationBalance`. */ 2608 size_t heapLeafCount; 2609 2610 size_t sparseBranchAllocatedSizeSum; 2611 2612 size_t sparseLeaf1AllocatedSizeSum; 2613 } 2614 2615 /** Destructively expand `curr` to a branch node able to store 2616 `capacityIncrement` more sub-nodes. 2617 */ 2618 Branch expand(SparseBranch* curr, size_t capacityIncrement = 1) @safe pure nothrow @nogc 2619 { 2620 typeof(return) next; 2621 assert(curr.subCount < radix); // we shouldn't expand beyond radix 2622 if (curr.empty) // if curr also empty length capacity must be zero 2623 { 2624 next = constructVariableSizeNode!(typeof(*curr))(1, curr); // so allocate one 2625 } 2626 else if (curr.subCount + capacityIncrement <= curr.maxCapacity) // if we can expand to curr 2627 { 2628 immutable requiredCapacity = curr.subCapacity + capacityIncrement; 2629 auto next_ = constructVariableSizeNode!(typeof(*curr))(requiredCapacity, curr); 2630 assert(next_.subCapacity >= requiredCapacity); 2631 next = next_; 2632 } 2633 else 2634 { 2635 next = constructFixedSizeNode!(DenseBranch*)(curr); 2636 } 2637 freeNode(curr); 2638 return next; 2639 } 2640 2641 /** Destructively expand `curr` to a leaf node able to store 2642 `capacityIncrement` more sub-nodes. 2643 */ 2644 Leaf1!Value expand(SparseLeaf1!Value* curr, size_t capacityIncrement = 1) @safe pure nothrow @nogc 2645 { 2646 typeof(return) next; 2647 assert(curr.length < radix); // we shouldn't expand beyond radix 2648 if (curr.empty) // if curr also empty length capacity must be zero 2649 { 2650 next = constructVariableSizeNode!(typeof(*curr))(capacityIncrement); // make room for at least one 2651 } 2652 else if (curr.length + capacityIncrement <= curr.maxCapacity) // if we can expand to curr 2653 { 2654 immutable requiredCapacity = curr.capacity + capacityIncrement; 2655 static if (isValue) 2656 { 2657 auto next_ = constructVariableSizeNode!(typeof(*curr))(requiredCapacity, curr.ixs, curr.values); 2658 } 2659 else 2660 { 2661 auto next_ = constructVariableSizeNode!(typeof(*curr))(requiredCapacity, curr.ixs); 2662 } 2663 assert(next_.capacity >= requiredCapacity); 2664 next = next_; 2665 } 2666 else 2667 { 2668 static if (isValue) 2669 { 2670 next = constructFixedSizeNode!(DenseLeaf1!Value*)(curr.ixs, curr.values); // TODO make use of sortedness of `curr.keys`? 2671 } 2672 else 2673 { 2674 next = constructFixedSizeNode!(DenseLeaf1!Value*)(curr.ixs); // TODO make use of sortedness of `curr.keys`? 2675 } 2676 } 2677 freeNode(curr); 2678 return next; 2679 } 2680 2681 /// ditto 2682 2683 Branch setSub(SparseBranch* curr, UIx subIx, Node subNode) @safe pure nothrow @nogc 2684 { 2685 // debug if (willFail) { dbg("WILL FAIL: subIx:", subIx); } 2686 size_t insertionIndex; 2687 ModStatus modStatus; 2688 curr = curr.reconstructingInsert(IxSub(subIx, subNode), modStatus, insertionIndex); 2689 if (modStatus == ModStatus.maxCapacityReached) // try insert and if it fails 2690 { 2691 // we need to expand because `curr` is full 2692 auto next = expand(curr); 2693 assert(getSub(next, subIx) == Node.init); // key slot should be unoccupied 2694 return setSub(next, subIx, subNode); 2695 } 2696 return Branch(curr); 2697 } 2698 2699 /// ditto 2700 Branch setSub(DenseBranch* curr, UIx subIx, Node subNode) @safe pure nothrow @nogc 2701 { 2702 pragma(inline, true); 2703 debug assert(!curr.subNodes[subIx], "sub-Node already set "); 2704 // "sub-Node at index " ~ subIx.to!string ~ 2705 // " already set to " ~ subNode.to!string); 2706 curr.subNodes[subIx] = subNode; 2707 return Branch(curr); 2708 } 2709 2710 /** Set sub-`Node` of branch `Node curr` at index `ix` to `subNode`. */ 2711 Branch setSub(Branch curr, UIx subIx, Node subNode) @safe pure nothrow @nogc 2712 { 2713 version(LDC) pragma(inline, true); 2714 final switch (curr.typeIx) with (Branch.Ix) 2715 { 2716 case ix_SparseBranchPtr: return setSub(curr.as!(SparseBranch*), subIx, subNode); 2717 case ix_DenseBranchPtr: return setSub(curr.as!(DenseBranch*), subIx, subNode); 2718 case undefined: assert(false); 2719 } 2720 } 2721 2722 /** Get sub-`Node` of branch `Node curr` at index `subIx`. */ 2723 Node getSub(Branch curr, UIx subIx) @safe pure nothrow @nogc 2724 { 2725 version(LDC) pragma(inline, true); 2726 final switch (curr.typeIx) with (Branch.Ix) 2727 { 2728 case ix_SparseBranchPtr: return getSub(curr.as!(SparseBranch*), subIx); 2729 case ix_DenseBranchPtr: return getSub(curr.as!(DenseBranch*), subIx); 2730 case undefined: assert(false); 2731 } 2732 } 2733 /// ditto 2734 Node getSub(SparseBranch* curr, UIx subIx) @safe pure nothrow @nogc 2735 { 2736 version(LDC) pragma(inline, true); 2737 if (auto subNode = curr.subNodeAt(subIx)) { return subNode; } 2738 return Node.init; 2739 } 2740 /// ditto 2741 Node getSub(DenseBranch* curr, UIx subIx) @safe pure nothrow @nogc 2742 { 2743 pragma(inline, true); 2744 auto sub = curr.subNodes[subIx]; 2745 debug curr.subNodes[subIx] = Node.init; // zero it to prevent multiple references 2746 return sub; 2747 } 2748 2749 /** Get leaves of node `curr`. */ 2750 inout(Leaf1!Value) getLeaf1(inout Branch curr) @safe pure nothrow @nogc 2751 { 2752 version(LDC) pragma(inline, true); 2753 final switch (curr.typeIx) with (Branch.Ix) 2754 { 2755 case ix_SparseBranchPtr: return curr.as!(SparseBranch*).leaf1; 2756 case ix_DenseBranchPtr: return curr.as!(DenseBranch*).leaf1; 2757 case undefined: assert(false); 2758 } 2759 } 2760 2761 /** Set direct leaves node of node `curr` to `leaf1`. */ 2762 void setLeaf1(Branch curr, Leaf1!Value leaf1) @safe pure nothrow @nogc 2763 { 2764 version(LDC) pragma(inline, true); 2765 final switch (curr.typeIx) with (Branch.Ix) 2766 { 2767 case ix_SparseBranchPtr: curr.as!(SparseBranch*).leaf1 = leaf1; break; 2768 case ix_DenseBranchPtr: curr.as!(DenseBranch*).leaf1 = leaf1; break; 2769 case undefined: assert(false); 2770 } 2771 } 2772 2773 /** Get prefix of node `curr`. */ 2774 auto getPrefix(inout Branch curr) @safe pure nothrow @nogc 2775 { 2776 version(LDC) pragma(inline, true); 2777 final switch (curr.typeIx) with (Branch.Ix) 2778 { 2779 case ix_SparseBranchPtr: return curr.as!(SparseBranch*).prefix[]; 2780 case ix_DenseBranchPtr: return curr.as!(DenseBranch*).prefix[]; 2781 case undefined: assert(false); 2782 } 2783 } 2784 2785 /** Set prefix of branch node `curr` to `prefix`. */ 2786 void setPrefix(Branch curr, const Ix[] prefix) @safe pure nothrow @nogc 2787 { 2788 pragma(inline, true); 2789 final switch (curr.typeIx) with (Branch.Ix) 2790 { 2791 case ix_SparseBranchPtr: curr.as!(SparseBranch*).prefix = typeof(curr.as!(SparseBranch*).prefix)(prefix); break; 2792 case ix_DenseBranchPtr: curr.as!(DenseBranch*).prefix = typeof(curr.as!(DenseBranch*).prefix)(prefix); break; 2793 case undefined: assert(false); 2794 } 2795 } 2796 2797 /** Pop `n` from prefix. */ 2798 void popFrontNPrefix(Branch curr, size_t n) @safe pure nothrow @nogc 2799 { 2800 version(LDC) pragma(inline, true); 2801 final switch (curr.typeIx) with (Branch.Ix) 2802 { 2803 case ix_SparseBranchPtr: curr.as!(SparseBranch*).prefix.popFrontN(n); break; 2804 case ix_DenseBranchPtr: curr.as!(DenseBranch*).prefix.popFrontN(n); break; 2805 case undefined: assert(false); 2806 } 2807 } 2808 2809 /** Get number of sub-nodes of node `curr`. */ 2810 SubCount getSubCount(inout Branch curr) @safe pure nothrow @nogc 2811 { 2812 version(LDC) pragma(inline, true); 2813 final switch (curr.typeIx) with (Branch.Ix) 2814 { 2815 case ix_SparseBranchPtr: return cast(typeof(return))curr.as!(SparseBranch*).subCount; 2816 case ix_DenseBranchPtr: return cast(typeof(return))curr.as!(DenseBranch*).subCount; 2817 case undefined: assert(false); 2818 } 2819 } 2820 2821 static if (isValue) 2822 { 2823 @safe pure nothrow @nogc: 2824 2825 /** Returns: `true` if `key` is stored under `curr`, `false` otherwise. */ 2826 inout(Value*) containsAt(inout Leaf1!Value curr, UKey key) 2827 { 2828 version(LDC) pragma(inline, true); 2829 // debug if (willFail) { dbg("curr:", curr); } 2830 // debug if (willFail) { dbg("key:", key); } 2831 switch (curr.typeIx) with (Leaf1!Value.Ix) 2832 { 2833 case undefined: return null; 2834 case ix_SparseLeaf1Ptr: return key.length == 1 ? curr.as!(SparseLeaf1!Value*).contains(UIx(key[0])) : null; 2835 case ix_DenseLeaf1Ptr: return key.length == 1 ? curr.as!(DenseLeaf1!Value*).contains(UIx(key[0])) : null; 2836 default: assert(false); 2837 } 2838 } 2839 2840 /// ditto 2841 inout(Value*) containsAt(inout Node curr, UKey key) /* TODO make @safe */ @trusted 2842 { 2843 assert(key.length); 2844 // debug if (willFail) { dbg("key:", key); } 2845 import nxt.array_algorithm : skipOver; 2846 switch (curr.typeIx) with (Node.Ix) 2847 { 2848 case undefined: return null; 2849 case ix_SparseLeaf1Ptr: return key.length == 1 ? curr.as!(SparseLeaf1!Value*).contains(UIx(key[0])) : null; 2850 case ix_DenseLeaf1Ptr: return key.length == 1 ? curr.as!(DenseLeaf1!Value*).contains(UIx(key[0])) : null; 2851 case ix_SparseBranchPtr: 2852 auto curr_ = curr.as!(SparseBranch*); 2853 if (key.skipOver(curr_.prefix[])) 2854 { 2855 return (key.length == 1 ? 2856 containsAt(curr_.leaf1, key) : // in leaf 2857 containsAt(curr_.subNodeAt(UIx(key[0])), key[1 .. $])); // recurse into branch tree 2858 } 2859 return null; 2860 case ix_DenseBranchPtr: 2861 auto curr_ = curr.as!(DenseBranch*); 2862 if (key.skipOver(curr_.prefix[])) 2863 { 2864 return (key.length == 1 ? 2865 containsAt(curr_.leaf1, key) : // in leaf 2866 containsAt(curr_.subNodes[UIx(key[0])], key[1 .. $])); // recurse into branch tree 2867 } 2868 return null; 2869 default: assert(false); 2870 } 2871 } 2872 } 2873 else 2874 { 2875 @safe pure nothrow @nogc: 2876 const: 2877 2878 /** Returns: `true` if `key` is stored under `curr`, `false` otherwise. */ 2879 bool containsAt(Leaf1!Value curr, UKey key) 2880 { 2881 // debug if (willFail) { dbg("key:", key); } 2882 final switch (curr.typeIx) with (Leaf1!Value.Ix) 2883 { 2884 case undefined: return false; 2885 case ix_HeptLeaf1: return curr.as!(HeptLeaf1).contains(key); 2886 case ix_SparseLeaf1Ptr: return key.length == 1 && curr.as!(SparseLeaf1!Value*).contains(UIx(key[0])); 2887 case ix_DenseLeaf1Ptr: return key.length == 1 && curr.as!(DenseLeaf1!Value*).contains(UIx(key[0])); 2888 } 2889 } 2890 /// ditto 2891 bool containsAt(Node curr, UKey key) /* TODO make @safe */ @trusted 2892 { 2893 assert(key.length); 2894 // debug if (willFail) { dbg("key:", key); } 2895 import nxt.array_algorithm : skipOver; 2896 final switch (curr.typeIx) with (Node.Ix) 2897 { 2898 case undefined: return false; 2899 case ix_OneLeafMax7: return curr.as!(OneLeafMax7).contains(key); 2900 case ix_TwoLeaf3: return curr.as!(TwoLeaf3).contains(key); 2901 case ix_TriLeaf2: return curr.as!(TriLeaf2).contains(key); 2902 case ix_HeptLeaf1: return curr.as!(HeptLeaf1).contains(key); 2903 case ix_SparseLeaf1Ptr: 2904 return key.length == 1 && curr.as!(SparseLeaf1!Value*).contains(UIx(key[0])); 2905 case ix_DenseLeaf1Ptr: 2906 return key.length == 1 && curr.as!(DenseLeaf1!Value*).contains(UIx(key[0])); 2907 case ix_SparseBranchPtr: 2908 auto curr_ = curr.as!(SparseBranch*); 2909 return (key.skipOver(curr_.prefix) && // matching prefix 2910 (key.length == 1 ? 2911 containsAt(curr_.leaf1, key) : // in leaf 2912 containsAt(curr_.subNodeAt(UIx(key[0])), key[1 .. $]))); // recurse into branch tree 2913 case ix_DenseBranchPtr: 2914 auto curr_ = curr.as!(DenseBranch*); 2915 return (key.skipOver(curr_.prefix) && // matching prefix 2916 (key.length == 1 ? 2917 containsAt(curr_.leaf1, key) : // in leaf 2918 containsAt(curr_.subNodes[UIx(key[0])], key[1 .. $]))); // recurse into branch tree 2919 } 2920 } 2921 } 2922 2923 inout(Node) prefixAt(inout Node curr, UKey keyPrefix, out UKey keyPrefixRest) /* TODO make @safe */ @trusted pure nothrow @nogc 2924 { 2925 import nxt.array_algorithm : startsWith; 2926 final switch (curr.typeIx) with (Node.Ix) 2927 { 2928 case undefined: return typeof(return).init; // terminate recursion 2929 case ix_OneLeafMax7: 2930 if (curr.as!(OneLeafMax7).key[].startsWith(keyPrefix)) { goto processHit; } 2931 break; 2932 case ix_TwoLeaf3: 2933 if (curr.as!(TwoLeaf3).keyLength >= keyPrefix.length) { goto processHit; } 2934 break; 2935 case ix_TriLeaf2: 2936 if (curr.as!(TriLeaf2).keyLength >= keyPrefix.length) { goto processHit; } 2937 break; 2938 case ix_HeptLeaf1: 2939 if (curr.as!(HeptLeaf1).keyLength >= keyPrefix.length) { goto processHit; } 2940 break; 2941 case ix_SparseLeaf1Ptr: 2942 case ix_DenseLeaf1Ptr: 2943 if (keyPrefix.length <= 1) { goto processHit; } 2944 break; 2945 case ix_SparseBranchPtr: 2946 auto curr_ = curr.as!(SparseBranch*); 2947 if (keyPrefix.startsWith(curr_.prefix[])) 2948 { 2949 immutable currPrefixLength = curr_.prefix.length; 2950 if (keyPrefix.length == currPrefixLength || // if no more prefix 2951 (curr_.leaf1 && // both leaf1 2952 curr_.subCount)) // and sub-nodes 2953 { 2954 goto processHit; 2955 } 2956 else if (curr_.subCount == 0) // only leaf1 2957 { 2958 return prefixAt(Node(curr_.leaf1), 2959 keyPrefix[currPrefixLength .. $], 2960 keyPrefixRest); 2961 } 2962 else // only sub-node(s) 2963 { 2964 return prefixAt(curr_.subNodeAt(UIx(keyPrefix[currPrefixLength])), 2965 keyPrefix[currPrefixLength + 1 .. $], 2966 keyPrefixRest); 2967 } 2968 } 2969 break; 2970 case ix_DenseBranchPtr: 2971 auto curr_ = curr.as!(DenseBranch*); 2972 if (keyPrefix.startsWith(curr_.prefix[])) 2973 { 2974 immutable currPrefixLength = curr_.prefix.length; 2975 if (keyPrefix.length == currPrefixLength || // if no more prefix 2976 (curr_.leaf1 && // both leaf1 2977 curr_.subCount)) // and sub-nodes 2978 { 2979 goto processHit; 2980 } 2981 else if (curr_.subCount == 0) // only leaf1 2982 { 2983 return prefixAt(Node(curr_.leaf1), 2984 keyPrefix[currPrefixLength .. $], 2985 keyPrefixRest); 2986 } 2987 else // only sub-node(s) 2988 { 2989 return prefixAt(curr_.subNodes[UIx(keyPrefix[currPrefixLength])], 2990 keyPrefix[currPrefixLength + 1 .. $], 2991 keyPrefixRest); 2992 } 2993 } 2994 break; 2995 } 2996 return typeof(return).init; 2997 processHit: 2998 keyPrefixRest = keyPrefix; 2999 return curr; 3000 } 3001 3002 inout(Node) matchCommonPrefixAt(inout Node curr, UKey key, out UKey keyRest) /* TODO make @safe */ @trusted pure nothrow @nogc 3003 { 3004 // dbg(curr.typeIx); 3005 import nxt.array_algorithm : startsWith; 3006 final switch (curr.typeIx) with (Node.Ix) 3007 { 3008 case undefined: return typeof(return).init; // terminate recursion 3009 case ix_OneLeafMax7: 3010 case ix_TwoLeaf3: 3011 case ix_TriLeaf2: 3012 case ix_HeptLeaf1: 3013 case ix_SparseLeaf1Ptr: 3014 case ix_DenseLeaf1Ptr: 3015 goto processHit; 3016 case ix_SparseBranchPtr: 3017 auto curr_ = curr.as!(SparseBranch*); 3018 // dbg(key); 3019 // dbg(curr_.prefix[]); 3020 if (key.startsWith(curr_.prefix[])) 3021 { 3022 immutable currPrefixLength = curr_.prefix.length; 3023 if (key.length == currPrefixLength || // if no more prefix 3024 (curr_.leaf1 && // both leaf1 3025 curr_.subCount)) // and sub-nodes 3026 { 3027 goto processHit; 3028 } 3029 else if (curr_.subCount == 0) // only leaf1 3030 { 3031 return matchCommonPrefixAt(Node(curr_.leaf1), 3032 key[currPrefixLength .. $], 3033 keyRest); 3034 } 3035 else if (curr_.subCount == 1) // only one sub node 3036 { 3037 return matchCommonPrefixAt(curr_.subNodeAt(UIx(key[currPrefixLength])), 3038 key[currPrefixLength + 1 .. $], 3039 keyRest); 3040 } 3041 else 3042 { 3043 goto processHit; 3044 } 3045 } 3046 break; 3047 case ix_DenseBranchPtr: 3048 auto curr_ = curr.as!(DenseBranch*); 3049 if (key.startsWith(curr_.prefix[])) 3050 { 3051 immutable currPrefixLength = curr_.prefix.length; 3052 if (key.length == currPrefixLength || // if no more prefix 3053 (curr_.leaf1 && // both leaf1 3054 curr_.subCount)) // and sub-nodes 3055 { 3056 goto processHit; 3057 } 3058 else if (curr_.subCount == 0) // only leaf1 3059 { 3060 return matchCommonPrefixAt(Node(curr_.leaf1), 3061 key[currPrefixLength .. $], 3062 keyRest); 3063 } 3064 else if (curr_.subCount == 1) // only one sub node 3065 { 3066 return matchCommonPrefixAt(curr_.subNodes[UIx(key[currPrefixLength])], 3067 key[currPrefixLength + 1 .. $], 3068 keyRest); 3069 } 3070 else 3071 { 3072 goto processHit; 3073 } 3074 } 3075 break; 3076 } 3077 return typeof(return).init; 3078 processHit: 3079 keyRest = key; 3080 return curr; 3081 } 3082 3083 size_t countHeapNodesAt(Node curr) @safe pure nothrow @nogc 3084 { 3085 size_t count = 0; 3086 final switch (curr.typeIx) with (Node.Ix) 3087 { 3088 case undefined: break; // propagate undefined 3089 case ix_OneLeafMax7: break; 3090 case ix_TwoLeaf3: break; 3091 case ix_TriLeaf2: break; 3092 case ix_HeptLeaf1: break; 3093 case ix_SparseLeaf1Ptr: 3094 case ix_DenseLeaf1Ptr: 3095 ++count; 3096 break; 3097 case ix_SparseBranchPtr: 3098 auto curr_ = curr.as!(SparseBranch*); 3099 ++count; 3100 foreach (subNode; curr_.subNodeSlots[0 .. curr_.subCount]) 3101 { 3102 if (subNode) { count += countHeapNodesAt(subNode); } 3103 } 3104 break; 3105 case ix_DenseBranchPtr: 3106 ++count; 3107 auto curr_ = curr.as!(DenseBranch*); 3108 foreach (subNode; curr_.subNodes) 3109 { 3110 if (subNode) { count += countHeapNodesAt(subNode); } 3111 } 3112 break; 3113 } 3114 return count; 3115 } 3116 3117 /** Returns a duplicate of this tree with root at `curr`. 3118 Shallowly duplicates the values in the map case. 3119 */ 3120 Leaf1!Value dupAt(Leaf1!Value curr) @safe pure nothrow @nogc 3121 { 3122 final switch (curr.typeIx) with (Leaf1!Value.Ix) 3123 { 3124 case undefined: // propagate undefined 3125 case ix_HeptLeaf1: return curr; // value semantics so just copy 3126 case ix_SparseLeaf1Ptr: return typeof(return)(curr.as!(SparseLeaf1!Value*).dup); 3127 case ix_DenseLeaf1Ptr: return typeof(return)(curr.as!(DenseLeaf1!Value*).dup); 3128 } 3129 } 3130 3131 /** Returns a duplicate of this tree with root at `curr`. 3132 Shallowly duplicates the values in the map case. 3133 */ 3134 Node dupAt(Node curr) @safe pure nothrow @nogc 3135 { 3136 final switch (curr.typeIx) with (Node.Ix) 3137 { 3138 case undefined: // propagate undefined 3139 case ix_OneLeafMax7: 3140 case ix_TwoLeaf3: 3141 case ix_TriLeaf2: 3142 case ix_HeptLeaf1: return curr; // value semantics so just copy 3143 case ix_SparseLeaf1Ptr: return typeof(return)(curr.as!(SparseLeaf1!Value*).dup); 3144 case ix_DenseLeaf1Ptr: return typeof(return)(curr.as!(DenseLeaf1!Value*).dup); 3145 case ix_SparseBranchPtr: return typeof(return)(curr.as!(SparseBranch*).dup); 3146 case ix_DenseBranchPtr: return typeof(return)(curr.as!(DenseBranch*).dup); 3147 } 3148 } 3149 3150 static if (!isValue) 3151 { 3152 Node insertNew(UKey key, out ElementRef elementRef) @safe pure nothrow @nogc 3153 { 3154 Node next; 3155 // debug if (willFail) { dbg("WILL FAIL: key:", key); } 3156 switch (key.length) 3157 { 3158 case 0: assert(false, "key must not be empty"); // return elementRef = Node(constructFixedSizeNode!(OneLeafMax7)()); 3159 case 1: next = Node(constructFixedSizeNode!(HeptLeaf1)(key[0])); break; 3160 case 2: next = Node(constructFixedSizeNode!(TriLeaf2)(key)); break; 3161 case 3: next = Node(constructFixedSizeNode!(TwoLeaf3)(key)); break; 3162 default: 3163 if (key.length <= OneLeafMax7.capacity) 3164 { 3165 next = Node(constructFixedSizeNode!(OneLeafMax7)(key)); 3166 break; 3167 } 3168 else // key doesn't fit in a `OneLeafMax7` 3169 { 3170 return Node(insertNewBranch(key, elementRef)); 3171 } 3172 } 3173 elementRef = ElementRef(next, 3174 UIx(0), // always first index 3175 ModStatus.added); 3176 return next; 3177 } 3178 } 3179 3180 Branch insertNewBranch(Elt!Value elt, out ElementRef elementRef) @safe pure nothrow @nogc 3181 { 3182 // debug if (willFail) { dbg("WILL FAIL: elt:", elt); } 3183 auto key = eltKey!Value(elt); 3184 assert(key.length); 3185 immutable prefixLength = min(key.length - 1, // all but last Ix of key 3186 DefaultBranch.prefixCapacity); // as much as possible of key in branch prefix 3187 auto prefix = key[0 .. prefixLength]; 3188 typeof(return) next = insertAtBelowPrefix(Branch(constructVariableSizeNode!(DefaultBranch)(1, prefix)), 3189 eltKeyDropExactly!Value(elt, prefixLength), elementRef); 3190 assert(elementRef); 3191 return next; 3192 } 3193 3194 /** Insert `key` into sub-tree under root `curr`. */ 3195 Node insertAt(Node curr, Elt!Value elt, out ElementRef elementRef) @safe pure nothrow @nogc 3196 { 3197 auto key = eltKey!Value(elt); 3198 // debug if (willFail) { dbg("WILL FAIL: key:", key, " curr:", curr); } 3199 assert(key.length); 3200 3201 if (!curr) // if no existing `Node` to insert at 3202 { 3203 static if (isValue) 3204 { 3205 auto next = Node(insertNewBranch(elt, elementRef)); 3206 } 3207 else 3208 { 3209 auto next = insertNew(key, elementRef); 3210 } 3211 assert(elementRef); // must be added to new Node 3212 return next; 3213 } 3214 else 3215 { 3216 final switch (curr.typeIx) with (Node.Ix) 3217 { 3218 case undefined: return typeof(return).init; 3219 case ix_OneLeafMax7: 3220 static if (isValue) 3221 { 3222 assert(false); 3223 } 3224 else 3225 { 3226 return insertAt(curr.as!(OneLeafMax7), key, elementRef); 3227 } 3228 case ix_TwoLeaf3: 3229 static if (isValue) 3230 { 3231 assert(false); 3232 } 3233 else 3234 { 3235 return insertAt(curr.as!(TwoLeaf3), key, elementRef); 3236 } 3237 case ix_TriLeaf2: 3238 static if (isValue) 3239 { 3240 assert(false); 3241 } 3242 else 3243 { 3244 return insertAt(curr.as!(TriLeaf2), key, elementRef); 3245 } 3246 case ix_HeptLeaf1: 3247 static if (isValue) 3248 { 3249 assert(false); 3250 } 3251 else 3252 { 3253 return insertAt(curr.as!(HeptLeaf1), key, elementRef); 3254 } 3255 case ix_SparseLeaf1Ptr: 3256 return insertAtLeaf(Leaf1!Value(curr.as!(SparseLeaf1!Value*)), elt, elementRef); // TODO use toLeaf(curr) 3257 case ix_DenseLeaf1Ptr: 3258 return insertAtLeaf(Leaf1!Value(curr.as!(DenseLeaf1!Value*)), elt, elementRef); // TODO use toLeaf(curr) 3259 case ix_SparseBranchPtr: 3260 // debug if (willFail) { dbg("WILL FAIL: currPrefix:", curr.as!(SparseBranch*).prefix); } 3261 return Node(insertAtAbovePrefix(Branch(curr.as!(SparseBranch*)), elt, elementRef)); 3262 case ix_DenseBranchPtr: 3263 return Node(insertAtAbovePrefix(Branch(curr.as!(DenseBranch*)), elt, elementRef)); 3264 } 3265 } 3266 } 3267 3268 /** Insert `key` into sub-tree under branch `curr` above prefix, that is 3269 the prefix of `curr` is stripped from `key` prior to insertion. */ 3270 Branch insertAtAbovePrefix(Branch curr, Elt!Value elt, out ElementRef elementRef) @safe pure nothrow @nogc 3271 { 3272 auto key = eltKey!Value(elt); 3273 assert(key.length); 3274 3275 import std.algorithm.searching : commonPrefix; 3276 auto currPrefix = getPrefix(curr); 3277 auto matchedKeyPrefix = commonPrefix(key, currPrefix); 3278 3279 // debug if (willFail) { dbg("WILL FAIL: key:", key, 3280 // " curr:", curr, 3281 // " currPrefix:", getPrefix(curr), 3282 // " matchedKeyPrefix:", matchedKeyPrefix); } 3283 3284 if (matchedKeyPrefix.length == 0) // no prefix key match 3285 { 3286 if (currPrefix.length == 0) // no current prefix 3287 { 3288 // debug if (willFail) { dbg("WILL FAIL"); } 3289 // NOTE: prefix:"", key:"cd" 3290 return insertAtBelowPrefix(curr, elt, elementRef); 3291 } 3292 else // if (currPrefix.length != 0) // non-empty current prefix 3293 { 3294 // NOTE: prefix:"ab", key:"cd" 3295 immutable currSubIx = UIx(currPrefix[0]); // subIx = 'a' 3296 if (currPrefix.length == 1 && getSubCount(curr) == 0) // if `curr`-prefix become empty and only leaf pointer 3297 { 3298 // debug if (willFail) { dbg("WILL FAIL"); } 3299 popFrontNPrefix(curr, 1); 3300 curr = setSub(curr, currSubIx, Node(getLeaf1(curr))); // move it to sub 3301 setLeaf1(curr, Leaf1!Value.init); 3302 3303 return insertAtBelowPrefix(curr, elt, elementRef); // directly call below because `curr`-prefix is now empty 3304 } 3305 else 3306 { 3307 // debug if (willFail) { dbg("WILL FAIL"); } 3308 popFrontNPrefix(curr, 1); 3309 auto next = constructVariableSizeNode!(DefaultBranch)(2, null, IxSub(currSubIx, Node(curr))); 3310 return insertAtAbovePrefix(Branch(next), elt, elementRef); 3311 } 3312 } 3313 } 3314 else if (matchedKeyPrefix.length < key.length) 3315 { 3316 if (matchedKeyPrefix.length == currPrefix.length) 3317 { 3318 // debug if (willFail) { dbg("WILL FAIL"); } 3319 // NOTE: key is an extension of prefix: prefix:"ab", key:"abcd" 3320 return insertAtBelowPrefix(curr, eltKeyDropExactly!Value(elt, currPrefix.length), elementRef); 3321 } 3322 else 3323 { 3324 // debug if (willFail) { dbg("WILL FAIL"); } 3325 // NOTE: prefix and key share beginning: prefix:"ab11", key:"ab22" 3326 immutable currSubIx = UIx(currPrefix[matchedKeyPrefix.length]); // need index first before we modify curr.prefix 3327 popFrontNPrefix(curr, matchedKeyPrefix.length + 1); 3328 auto next = constructVariableSizeNode!(DefaultBranch)(2, matchedKeyPrefix, IxSub(currSubIx, Node(curr))); 3329 return insertAtBelowPrefix(Branch(next), eltKeyDropExactly!Value(elt, matchedKeyPrefix.length), elementRef); 3330 } 3331 } 3332 else // if (matchedKeyPrefix.length == key.length) 3333 { 3334 // debug if (willFail) { dbg("WILL FAIL"); } 3335 assert(matchedKeyPrefix.length == key.length); 3336 if (matchedKeyPrefix.length < currPrefix.length) 3337 { 3338 // NOTE: prefix is an extension of key: prefix:"abcd", key:"ab" 3339 assert(matchedKeyPrefix.length); 3340 immutable nextPrefixLength = matchedKeyPrefix.length - 1; 3341 immutable currSubIx = UIx(currPrefix[nextPrefixLength]); // need index first 3342 popFrontNPrefix(curr, matchedKeyPrefix.length); // drop matchedKeyPrefix plus index to next super branch 3343 auto next = constructVariableSizeNode!(DefaultBranch)(2, matchedKeyPrefix[0 .. $ - 1], 3344 IxSub(currSubIx, Node(curr))); 3345 return insertAtBelowPrefix(Branch(next), eltKeyDropExactly!Value(elt, nextPrefixLength), elementRef); 3346 } 3347 else /* if (matchedKeyPrefix.length == currPrefix.length) and in turn 3348 if (key.length == currPrefix.length */ 3349 { 3350 // NOTE: prefix equals key: prefix:"abcd", key:"abcd" 3351 assert(matchedKeyPrefix.length); 3352 immutable currSubIx = UIx(currPrefix[matchedKeyPrefix.length - 1]); // need index first 3353 popFrontNPrefix(curr, matchedKeyPrefix.length); // drop matchedKeyPrefix plus index to next super branch 3354 auto next = constructVariableSizeNode!(DefaultBranch)(2, matchedKeyPrefix[0 .. $ - 1], 3355 IxSub(currSubIx, Node(curr))); 3356 static if (isValue) 3357 { 3358 return insertAtLeaf1(Branch(next), UIx(key[$ - 1]), elt.value, elementRef); 3359 } 3360 else 3361 { 3362 return insertAtLeaf1(Branch(next), UIx(key[$ - 1]), elementRef); 3363 } 3364 } 3365 } 3366 } 3367 3368 /** Like `insertAtAbovePrefix` but also asserts that `key` is 3369 currently not stored under `curr`. */ 3370 pragma(inline) Branch insertNewAtAbovePrefix(Branch curr, Elt!Value elt) @safe pure nothrow @nogc 3371 { 3372 pragma(inline, true); 3373 ElementRef elementRef; 3374 auto next = insertAtAbovePrefix(curr, elt, elementRef); 3375 assert(elementRef); 3376 return next; 3377 } 3378 3379 static if (isValue) 3380 { 3381 pragma(inline) Branch insertAtSubNode(Branch curr, UKey key, Value value, out ElementRef elementRef) @safe pure nothrow @nogc 3382 { 3383 // debug if (willFail) { dbg("WILL FAIL"); } 3384 immutable subIx = UIx(key[0]); 3385 return setSub(curr, subIx, 3386 insertAt(getSub(curr, subIx), // recurse 3387 Elt!Value(key[1 .. $], value), 3388 elementRef)); 3389 } 3390 } 3391 else 3392 { 3393 pragma(inline) Branch insertAtSubNode(Branch curr, UKey key, out ElementRef elementRef) @safe pure nothrow @nogc 3394 { 3395 // debug if (willFail) { dbg("WILL FAIL"); } 3396 immutable subIx = UIx(key[0]); 3397 return setSub(curr, subIx, 3398 insertAt(getSub(curr, subIx), // recurse 3399 key[1 .. $], 3400 elementRef)); 3401 } 3402 } 3403 3404 /** Insert `key` into sub-tree under branch `curr` below prefix, that is 3405 the prefix of `curr` is not stripped from `key` prior to 3406 insertion. */ 3407 Branch insertAtBelowPrefix(Branch curr, Elt!Value elt, out ElementRef elementRef) @safe pure nothrow @nogc 3408 { 3409 auto key = eltKey!Value(elt); 3410 assert(key.length); 3411 // debug if (willFail) { dbg("WILL FAIL: key:", key, 3412 // " curr:", curr, 3413 // " currPrefix:", getPrefix(curr), 3414 // " elementRef:", elementRef); } 3415 if (key.length == 1) 3416 { 3417 static if (isValue) 3418 { 3419 return insertAtLeaf1(curr, UIx(key[0]), elt.value, elementRef); 3420 } 3421 else 3422 { 3423 return insertAtLeaf1(curr, UIx(key[0]), elementRef); 3424 } 3425 } 3426 else // key.length >= 2 3427 { 3428 static if (isValue) 3429 { 3430 return insertAtSubNode(curr, key, elt.value, elementRef); 3431 } 3432 else 3433 { 3434 return insertAtSubNode(curr, key, elementRef); 3435 } 3436 } 3437 } 3438 3439 Branch insertNewAtBelowPrefix(Branch curr, Elt!Value elt) @safe pure nothrow @nogc 3440 { 3441 pragma(inline, true); 3442 ElementRef elementRef; 3443 auto next = insertAtBelowPrefix(curr, elt, elementRef); 3444 assert(elementRef); 3445 return next; 3446 } 3447 3448 Leaf1!Value insertIxAtLeaftoLeaf(Leaf1!Value curr, IxElt!Value elt, out ElementRef elementRef) @safe pure nothrow @nogc 3449 { 3450 auto key = eltIx!Value(elt); 3451 // debug if (willFail) { dbg("WILL FAIL: elt:", elt, 3452 // " curr:", curr, 3453 // " elementRef:", elementRef); } 3454 switch (curr.typeIx) with (Leaf1!Value.Ix) 3455 { 3456 case undefined: 3457 return typeof(return).init; 3458 case ix_HeptLeaf1: 3459 static if (isValue) 3460 { 3461 assert(false); 3462 } 3463 else 3464 { 3465 return insertAt(curr.as!(HeptLeaf1), key, elementRef); // possibly expanded to other Leaf1!Value 3466 } 3467 case ix_SparseLeaf1Ptr: 3468 SparseLeaf1!Value* curr_ = curr.as!(SparseLeaf1!Value*); 3469 size_t index; 3470 ModStatus modStatus; 3471 curr_ = curr_.reconstructingInsert(elt, modStatus, index); 3472 curr = Leaf1!Value(curr_); 3473 final switch (modStatus) 3474 { 3475 case ModStatus.unchanged: // already stored at `index` 3476 elementRef = ElementRef(Node(curr_), UIx(index), modStatus); 3477 return curr; 3478 case ModStatus.added: 3479 elementRef = ElementRef(Node(curr_), UIx(index), modStatus); 3480 return curr; 3481 case ModStatus.maxCapacityReached: 3482 auto next = insertIxAtLeaftoLeaf(expand(curr_, 1), // make room for one more 3483 elt, elementRef); 3484 assert(next.peek!(DenseLeaf1!Value*)); 3485 return next; 3486 case ModStatus.updated: 3487 elementRef = ElementRef(Node(curr_), UIx(index), modStatus); 3488 return curr; 3489 } 3490 case ix_DenseLeaf1Ptr: 3491 immutable modStatus = curr.as!(DenseLeaf1!Value*).insert(elt); 3492 static if (isValue) 3493 { 3494 immutable ix = elt.ix; 3495 } 3496 else 3497 { 3498 immutable ix = elt; 3499 } 3500 elementRef = ElementRef(Node(curr), ix, modStatus); 3501 break; 3502 default: 3503 assert(false, "Unsupported Leaf1!Value type " // ~ curr.typeIx.to!string 3504 ); 3505 } 3506 return curr; 3507 } 3508 3509 static if (isValue) 3510 { 3511 Branch insertAtLeaf1(Branch curr, UIx key, Value value, out ElementRef elementRef) @safe pure nothrow @nogc 3512 { 3513 // debug if (willFail) { dbg("WILL FAIL: key:", key, 3514 // " value:", value, 3515 // " curr:", curr, 3516 // " currPrefix:", getPrefix(curr), 3517 // " elementRef:", elementRef); } 3518 if (auto leaf = getLeaf1(curr)) 3519 { 3520 setLeaf1(curr, insertIxAtLeaftoLeaf(leaf, IxElt!Value(key, value), elementRef)); 3521 } 3522 else 3523 { 3524 Ix[1] ixs = [Ix(key)]; // TODO scope 3525 Value[1] values = [value]; // TODO scope 3526 auto leaf_ = constructVariableSizeNode!(SparseLeaf1!Value)(1, ixs, values); // needed for values 3527 elementRef = ElementRef(Node(leaf_), UIx(0), ModStatus.added); 3528 setLeaf1(curr, Leaf1!Value(leaf_)); 3529 } 3530 return curr; 3531 } 3532 } 3533 else 3534 { 3535 Branch insertAtLeaf1(Branch curr, UIx key, out ElementRef elementRef) @safe pure nothrow @nogc 3536 { 3537 // debug if (willFail) { dbg("WILL FAIL: key:", key, 3538 // " curr:", curr, 3539 // " currPrefix:", getPrefix(curr), 3540 // " elementRef:", elementRef); } 3541 if (auto leaf = getLeaf1(curr)) 3542 { 3543 setLeaf1(curr, insertIxAtLeaftoLeaf(leaf, key, elementRef)); 3544 } 3545 else 3546 { 3547 auto leaf_ = constructFixedSizeNode!(HeptLeaf1)(key); // can pack more efficiently when no value 3548 elementRef = ElementRef(Node(leaf_), UIx(0), ModStatus.added); 3549 setLeaf1(curr, Leaf1!Value(leaf_)); 3550 } 3551 return curr; 3552 } 3553 } 3554 3555 Node insertAtLeaf(Leaf1!Value curr, Elt!Value elt, out ElementRef elementRef) @safe pure nothrow @nogc 3556 { 3557 // debug if (willFail) { dbg("WILL FAIL: elt:", elt); } 3558 auto key = eltKey!Value(elt); 3559 assert(key.length); 3560 if (key.length == 1) 3561 { 3562 static if (isValue) 3563 { 3564 return Node(insertIxAtLeaftoLeaf(curr, IxElt!Value(UIx(key[0]), elt.value), elementRef)); 3565 } 3566 else 3567 { 3568 return Node(insertIxAtLeaftoLeaf(curr, UIx(key[0]), elementRef)); 3569 } 3570 } 3571 else 3572 { 3573 assert(key.length >= 2); 3574 immutable prefixLength = key.length - 2; // >= 0 3575 const nextPrefix = key[0 .. prefixLength]; 3576 auto next = constructVariableSizeNode!(DefaultBranch)(1, nextPrefix, curr); // one sub-node and one leaf 3577 return Node(insertAtBelowPrefix(Branch(next), eltKeyDropExactly!Value(elt, prefixLength), elementRef)); 3578 } 3579 } 3580 3581 static if (!isValue) 3582 { 3583 Node insertAt(OneLeafMax7 curr, UKey key, out ElementRef elementRef) @safe pure nothrow @nogc 3584 { 3585 assert(curr.key.length); 3586 // debug if (willFail) { dbg("WILL FAIL: key:", key, " curr.key:", curr.key); } 3587 3588 import std.algorithm.searching : commonPrefix; 3589 auto matchedKeyPrefix = commonPrefix(key, curr.key); 3590 if (curr.key.length == key.length) 3591 { 3592 if (matchedKeyPrefix.length == key.length) // curr.key, key and matchedKeyPrefix all equal 3593 { 3594 return Node(curr); // already stored in `curr` 3595 } 3596 else if (matchedKeyPrefix.length + 1 == key.length) // key and curr.key are both matchedKeyPrefix plus one extra 3597 { 3598 // TODO functionize: 3599 Node next; 3600 switch (matchedKeyPrefix.length) 3601 { 3602 case 0: 3603 next = constructFixedSizeNode!(HeptLeaf1)(curr.key[0], key[0]); 3604 elementRef = ElementRef(next, UIx(1), ModStatus.added); 3605 break; 3606 case 1: 3607 next = constructFixedSizeNode!(TriLeaf2)(curr.key, key); 3608 elementRef = ElementRef(next, UIx(1), ModStatus.added); 3609 break; 3610 case 2: 3611 next = constructFixedSizeNode!(TwoLeaf3)(curr.key, key); 3612 elementRef = ElementRef(next, UIx(1), ModStatus.added); 3613 break; 3614 default: 3615 const nextPrefix = matchedKeyPrefix[0 .. min(matchedKeyPrefix.length, 3616 DefaultBranch.prefixCapacity)]; // limit prefix branch capacity 3617 Branch nextBranch = constructVariableSizeNode!(DefaultBranch)(1 + 1, // `curr` and `key` 3618 nextPrefix); 3619 nextBranch = insertNewAtBelowPrefix(nextBranch, curr.key[nextPrefix.length .. $]); 3620 nextBranch = insertAtBelowPrefix(nextBranch, key[nextPrefix.length .. $], elementRef); 3621 assert(elementRef); 3622 next = Node(nextBranch); 3623 break; 3624 } 3625 freeNode(curr); 3626 return next; 3627 } 3628 } 3629 3630 return Node(insertAtAbovePrefix(expand(curr), key, elementRef)); 3631 } 3632 3633 Node insertAt(TwoLeaf3 curr, UKey key, out ElementRef elementRef) @safe pure nothrow @nogc 3634 { 3635 if (curr.keyLength == key.length) 3636 { 3637 if (curr.contains(key)) { return Node(curr); } 3638 if (!curr.keys.full) 3639 { 3640 assert(curr.keys.length == 1); 3641 elementRef = ElementRef(Node(curr), UIx(curr.keys.length), ModStatus.added); 3642 curr.keys.pushBack(key); 3643 return Node(curr); 3644 } 3645 } 3646 return Node(insertAtAbovePrefix(expand(curr), key, elementRef)); // NOTE stay at same (depth) 3647 } 3648 3649 Node insertAt(TriLeaf2 curr, UKey key, out ElementRef elementRef) @safe pure nothrow @nogc 3650 { 3651 if (curr.keyLength == key.length) 3652 { 3653 if (curr.contains(key)) { return Node(curr); } 3654 if (!curr.keys.full) 3655 { 3656 elementRef = ElementRef(Node(curr), UIx(curr.keys.length), ModStatus.added); 3657 curr.keys.pushBack(key); 3658 return Node(curr); 3659 } 3660 } 3661 return Node(insertAtAbovePrefix(expand(curr), key, elementRef)); // NOTE stay at same (depth) 3662 } 3663 3664 Leaf1!Value insertAt(HeptLeaf1 curr, UIx key, out ElementRef elementRef) @safe pure nothrow @nogc 3665 { 3666 if (curr.contains(key)) { return Leaf1!Value(curr); } 3667 if (!curr.keys.full) 3668 { 3669 elementRef = ElementRef(Node(curr), UIx(curr.keys.back), ModStatus.added); 3670 curr.keys.pushBack(cast(Ix)key); 3671 return Leaf1!Value(curr); 3672 } 3673 else // curr is full 3674 { 3675 assert(curr.keys.length == curr.capacity); 3676 3677 // pack `curr.keys` plus `key` into `nextKeys` 3678 Ix[curr.capacity + 1] nextKeys; 3679 nextKeys[0 .. curr.capacity] = curr.keys; 3680 nextKeys[curr.capacity] = cast(Ix)key; 3681 3682 import std.algorithm.sorting : sort; 3683 sort(nextKeys[]); // TODO move this sorting elsewhere 3684 3685 auto next = constructVariableSizeNode!(SparseLeaf1!Value)(nextKeys.length, nextKeys[]); 3686 elementRef = ElementRef(Node(next), UIx(curr.capacity), ModStatus.added); 3687 3688 freeNode(curr); 3689 return Leaf1!Value(next); 3690 } 3691 } 3692 3693 Node insertAt(HeptLeaf1 curr, UKey key, out ElementRef elementRef) 3694 @safe pure nothrow @nogc 3695 { 3696 if (curr.keyLength == key.length) 3697 { 3698 return Node(insertAt(curr, UIx(key[0]), elementRef)); // use `Ix key`-overload 3699 } 3700 return insertAt(Node(constructVariableSizeNode!(DefaultBranch)(1, Leaf1!Value(curr))), // current `key` 3701 key, elementRef); // NOTE stay at same (depth) 3702 } 3703 3704 /** Split `curr` using `prefix`. */ 3705 Node split(OneLeafMax7 curr, UKey prefix, UKey key) // TODO key here is a bit malplaced 3706 @safe pure nothrow @nogc 3707 { 3708 assert(key.length); 3709 3710 if (curr.key.length == key.length) // balanced tree possible 3711 { 3712 switch (curr.key.length) 3713 { 3714 case 1: 3715 if (prefix.length == 0) 3716 { 3717 freeNode(curr); 3718 return Node(constructFixedSizeNode!(HeptLeaf1)(curr.key)); // TODO removing parameter has no effect. why? 3719 } 3720 break; 3721 case 2: 3722 freeNode(curr); 3723 return Node(constructFixedSizeNode!(TriLeaf2)(curr.key)); 3724 case 3: 3725 freeNode(curr); 3726 return Node(constructFixedSizeNode!(TwoLeaf3)(curr.key)); 3727 default: 3728 break; 3729 } 3730 } 3731 3732 // default case 3733 Branch next = constructVariableSizeNode!(DefaultBranch)(1 + 1, prefix); // current plus one more 3734 next = insertNewAtBelowPrefix(next, curr.key[prefix.length .. $]); 3735 freeNode(curr); // remove old current 3736 3737 return Node(next); 3738 } 3739 3740 /** Destructively expand `curr` to make room for `capacityIncrement` more keys and return it. */ 3741 Branch expand(OneLeafMax7 curr, size_t capacityIncrement = 1) 3742 @safe pure nothrow @nogc 3743 { 3744 assert(curr.key.length >= 2); 3745 typeof(return) next; 3746 3747 if (curr.key.length <= DefaultBranch.prefixCapacity + 1) // if `key` fits in `prefix` of `DefaultBranch` 3748 { 3749 next = constructVariableSizeNode!(DefaultBranch)(1 + capacityIncrement, curr.key[0 .. $ - 1], // all but last 3750 Leaf1!Value(constructFixedSizeNode!(HeptLeaf1)(curr.key.back))); // last as a leaf 3751 } 3752 else // curr.key.length > DefaultBranch.prefixCapacity + 1 3753 { 3754 next = constructVariableSizeNode!(DefaultBranch)(1 + capacityIncrement, curr.key[0 .. DefaultBranch.prefixCapacity]); 3755 next = insertNewAtBelowPrefix(next, curr.key[DefaultBranch.prefixCapacity .. $]); 3756 } 3757 3758 freeNode(curr); 3759 return next; 3760 } 3761 3762 /** Destructively expand `curr` to make room for `capacityIncrement` more keys and return it. */ 3763 Branch expand(TwoLeaf3 curr, size_t capacityIncrement = 1) 3764 @safe pure nothrow @nogc 3765 { 3766 typeof(return) next; 3767 if (curr.keys.length == 1) // only one key 3768 { 3769 next = constructVariableSizeNode!(DefaultBranch)(1 + capacityIncrement); 3770 next = insertNewAtAbovePrefix(next, // current keys plus one more 3771 curr.keys.at!0); 3772 } 3773 else 3774 { 3775 next = constructVariableSizeNode!(DefaultBranch)(curr.keys.length + capacityIncrement, curr.prefix); 3776 // TODO functionize and optimize to insertNewAtAbovePrefix(next, curr.keys) 3777 foreach (key; curr.keys) 3778 { 3779 next = insertNewAtBelowPrefix(next, key[curr.prefix.length .. $]); 3780 } 3781 } 3782 freeNode(curr); 3783 return next; 3784 } 3785 3786 /** Destructively expand `curr` to make room for `capacityIncrement` more keys and return it. */ 3787 Branch expand(TriLeaf2 curr, size_t capacityIncrement = 1) 3788 @safe pure nothrow @nogc 3789 { 3790 typeof(return) next; 3791 if (curr.keys.length == 1) // only one key 3792 { 3793 next = constructVariableSizeNode!(DefaultBranch)(1 + capacityIncrement); // current keys plus one more 3794 next = insertNewAtAbovePrefix(next, curr.keys.at!0); 3795 } 3796 else 3797 { 3798 next = constructVariableSizeNode!(DefaultBranch)(curr.keys.length + capacityIncrement, curr.prefix); 3799 // TODO functionize and optimize to insertNewAtAbovePrefix(next, curr.keys) 3800 foreach (key; curr.keys) 3801 { 3802 next = insertNewAtBelowPrefix(next, key[curr.prefix.length .. $]); 3803 } 3804 } 3805 freeNode(curr); 3806 return next; 3807 } 3808 3809 /** Destructively expand `curr` making room for `nextKey` and return it. */ 3810 Node expand(HeptLeaf1 curr, size_t capacityIncrement = 1) 3811 { 3812 auto next = constructVariableSizeNode!(SparseLeaf1!Value)(curr.keys.length + capacityIncrement, curr.keys); 3813 freeNode(curr); 3814 return Node(next); 3815 } 3816 } 3817 3818 @safe pure nothrow @nogc 3819 { 3820 pragma(inline, true) void release(SparseLeaf1!Value* curr) { freeNode(curr); } 3821 pragma(inline, true) void release(DenseLeaf1!Value* curr) { freeNode(curr); } 3822 3823 void release(SparseBranch* curr) 3824 { 3825 foreach (immutable sub; curr.subNodes[0 .. curr.subCount]) 3826 { 3827 release(sub); // recurse branch 3828 } 3829 if (curr.leaf1) 3830 { 3831 release(curr.leaf1); // recurse leaf 3832 } 3833 freeNode(curr); 3834 } 3835 3836 void release(DenseBranch* curr) 3837 { 3838 foreach (immutable sub; curr.subNodes[].filter!(sub => sub)) // TODO use static foreach 3839 { 3840 release(sub); // recurse branch 3841 } 3842 if (curr.leaf1) 3843 { 3844 release(curr.leaf1); // recurse leaf 3845 } 3846 freeNode(curr); 3847 } 3848 3849 pragma(inline, true) void release(OneLeafMax7 curr) { freeNode(curr); } 3850 pragma(inline, true) void release(TwoLeaf3 curr) { freeNode(curr); } 3851 pragma(inline, true) void release(TriLeaf2 curr) { freeNode(curr); } 3852 pragma(inline, true) void release(HeptLeaf1 curr) { freeNode(curr); } 3853 3854 /** Release `Leaf1!Value curr`. */ 3855 void release(Leaf1!Value curr) 3856 { 3857 final switch (curr.typeIx) with (Leaf1!Value.Ix) 3858 { 3859 case undefined: break; // ignored 3860 case ix_HeptLeaf1: return release(curr.as!(HeptLeaf1)); 3861 case ix_SparseLeaf1Ptr: return release(curr.as!(SparseLeaf1!Value*)); 3862 case ix_DenseLeaf1Ptr: return release(curr.as!(DenseLeaf1!Value*)); 3863 } 3864 } 3865 3866 /** Release `Node curr`. */ 3867 void release(Node curr) 3868 { 3869 final switch (curr.typeIx) with (Node.Ix) 3870 { 3871 case undefined: break; // ignored 3872 case ix_OneLeafMax7: return release(curr.as!(OneLeafMax7)); 3873 case ix_TwoLeaf3: return release(curr.as!(TwoLeaf3)); 3874 case ix_TriLeaf2: return release(curr.as!(TriLeaf2)); 3875 case ix_HeptLeaf1: return release(curr.as!(HeptLeaf1)); 3876 case ix_SparseLeaf1Ptr: return release(curr.as!(SparseLeaf1!Value*)); 3877 case ix_DenseLeaf1Ptr: return release(curr.as!(DenseLeaf1!Value*)); 3878 case ix_SparseBranchPtr: return release(curr.as!(SparseBranch*)); 3879 case ix_DenseBranchPtr: return release(curr.as!(DenseBranch*)); 3880 } 3881 } 3882 3883 bool isHeapAllocatedNode(const Node curr) 3884 { 3885 final switch (curr.typeIx) with (Node.Ix) 3886 { 3887 case undefined: return false; 3888 case ix_OneLeafMax7: return false; 3889 case ix_TwoLeaf3: return false; 3890 case ix_TriLeaf2: return false; 3891 case ix_HeptLeaf1: return false; 3892 case ix_SparseLeaf1Ptr: return true; 3893 case ix_DenseLeaf1Ptr: return true; 3894 case ix_SparseBranchPtr: return true; 3895 case ix_DenseBranchPtr: return true; 3896 } 3897 } 3898 } 3899 3900 void printAt(Node curr, size_t depth, uint subIx = uint.max) @safe 3901 { 3902 import std.range : repeat; 3903 import std.stdio : write, writeln; 3904 3905 if (!curr) { return; } 3906 3907 foreach (immutable i; 0 .. depth) { write('-'); } // prefix 3908 if (subIx != uint.max) 3909 { 3910 import std.string : format; 3911 write(format("%.2X ", subIx)); 3912 } 3913 3914 final switch (curr.typeIx) with (Node.Ix) 3915 { 3916 case undefined: 3917 assert(false, "Trying to print Node.undefined"); 3918 case ix_OneLeafMax7: 3919 auto curr_ = curr.as!(OneLeafMax7); 3920 import std.conv : to; 3921 writeln(typeof(curr_).stringof, " #", curr_.key.length, ": ", curr_.to!string); 3922 break; 3923 case ix_TwoLeaf3: 3924 auto curr_ = curr.as!(TwoLeaf3); 3925 writeln(typeof(curr_).stringof, " #", curr_.keys.length, ": ", curr_.keys); 3926 break; 3927 case ix_TriLeaf2: 3928 auto curr_ = curr.as!(TriLeaf2); 3929 writeln(typeof(curr_).stringof, " #", curr_.keys.length, ": ", curr_.keys); 3930 break; 3931 case ix_HeptLeaf1: 3932 auto curr_ = curr.as!(HeptLeaf1); 3933 writeln(typeof(curr_).stringof, " #", curr_.keys.length, ": ", curr_.keys); 3934 break; 3935 case ix_SparseLeaf1Ptr: 3936 auto curr_ = curr.as!(SparseLeaf1!Value*); 3937 write(typeof(*curr_).stringof, " #", curr_.length, "/", curr_.capacity, " @", curr_); 3938 write(": "); 3939 bool other = false; 3940 foreach (immutable i, immutable ix; curr_.ixs) 3941 { 3942 string s; 3943 if (other) 3944 { 3945 s ~= keySeparator; 3946 } 3947 else 3948 { 3949 other = true; 3950 } 3951 import std.string : format; 3952 s ~= format("%.2X", ix); 3953 write(s); 3954 static if (isValue) 3955 { 3956 write("=>", curr_.values[i]); 3957 } 3958 } 3959 writeln(); 3960 break; 3961 case ix_DenseLeaf1Ptr: 3962 auto curr_ = curr.as!(DenseLeaf1!Value*); 3963 write(typeof(*curr_).stringof, " #", curr_.count, " @", curr_); 3964 write(": "); 3965 3966 // keys 3967 size_t ix = 0; 3968 bool other = false; 3969 if (curr_._ixBits.allOne) 3970 { 3971 write("ALL"); 3972 } 3973 else 3974 { 3975 foreach (immutable keyBit; curr_._ixBits[]) 3976 { 3977 string s; 3978 if (keyBit) 3979 { 3980 if (other) 3981 { 3982 s ~= keySeparator; 3983 } 3984 else 3985 { 3986 other = true; 3987 } 3988 import std.string : format; 3989 s ~= format("%.2X", ix); 3990 } 3991 write(s); 3992 static if (isValue) 3993 { 3994 write("=>", curr_.values[ix]); 3995 } 3996 ++ix; 3997 } 3998 } 3999 4000 writeln(); 4001 break; 4002 case ix_SparseBranchPtr: 4003 auto curr_ = curr.as!(SparseBranch*); 4004 write(typeof(*curr_).stringof, " #", curr_.subCount, "/", curr_.subCapacity, " @", curr_); 4005 if (!curr_.prefix.empty) { write(" prefix=", curr_.prefix.toString('_')); } 4006 writeln(":"); 4007 if (curr_.leaf1) 4008 { 4009 printAt(Node(curr_.leaf1), depth + 1); 4010 } 4011 foreach (immutable i, immutable subNode; curr_.subNodes) 4012 { 4013 printAt(subNode, depth + 1, cast(uint)curr_.subIxs[i]); 4014 } 4015 break; 4016 case ix_DenseBranchPtr: 4017 auto curr_ = curr.as!(DenseBranch*); 4018 write(typeof(*curr_).stringof, " #", curr_.subCount, "/", radix, " @", curr_); 4019 if (!curr_.prefix.empty) { write(" prefix=", curr_.prefix.toString('_')); } 4020 writeln(":"); 4021 if (curr_.leaf1) 4022 { 4023 printAt(Node(curr_.leaf1), depth + 1); 4024 } 4025 foreach (immutable i, immutable subNode; curr_.subNodes) 4026 { 4027 printAt(subNode, depth + 1, cast(uint)i); 4028 } 4029 4030 break; 4031 } 4032 } 4033 4034 struct RawRadixTree 4035 { 4036 alias NodeType = Node; 4037 alias BranchType = Branch; 4038 alias DefaultBranchType = DefaultBranch; 4039 alias ValueType = Value; 4040 alias RangeType = Range; 4041 alias StatsType = Stats; 4042 alias SparseBranchType = SparseBranch; 4043 alias DenseBranchType = DenseBranch; 4044 alias ElementRefType = ElementRef; 4045 4046 import core.lifetime : emplace; 4047 4048 /** Is `true` if this tree stores values of type `Value` along with keys. In 4049 other words: `this` is a $(I map) rather than a $(I set). 4050 */ 4051 alias hasValue = isValue; 4052 4053 Range opSlice() pure nothrow // TODO DIP-1000 scope 4054 { 4055 pragma(inline, true); 4056 return Range(_root, []); 4057 } 4058 4059 /** Returns a duplicate of this tree if present. 4060 Shallowly duplicates the values in the map case. 4061 */ 4062 typeof(this) dup() @trusted 4063 { 4064 pragma(inline, true); 4065 return typeof(return)(dupAt(_root), length); 4066 } 4067 4068 Stats usageHistograms() const 4069 { 4070 if (!_root) { return typeof(return).init; } 4071 typeof(return) stats; 4072 _root.calculate!(Value)(stats); 4073 return stats; 4074 } 4075 4076 @disable this(this); 4077 4078 ~this() @nogc 4079 { 4080 pragma(inline, true); 4081 release(_root); 4082 debug _root = Node.init; 4083 } 4084 4085 /** Removes all contents (elements). */ 4086 void clear() @nogc 4087 { 4088 pragma(inline, true); 4089 release(_root); 4090 _root = null; 4091 _length = 0; 4092 } 4093 4094 @safe pure nothrow @nogc 4095 { 4096 /** Returns: `true` if `key` is stored, `false` otherwise. */ 4097 inout(Node) prefix(UKey keyPrefix, out UKey keyPrefixRest) inout 4098 { 4099 pragma(inline, true); 4100 return prefixAt(_root, keyPrefix, keyPrefixRest); 4101 } 4102 4103 4104 /** Lookup deepest node having whose key starts with `key`. */ 4105 inout(Node) matchCommonPrefix(UKey key, out UKey keyRest) inout 4106 { 4107 pragma(inline, true); 4108 return matchCommonPrefixAt(_root, key, keyRest); 4109 } 4110 4111 static if (isValue) 4112 { 4113 /** Returns: `true` if `key` is stored, `false` otherwise. */ 4114 inout(Value*) contains(UKey key) inout 4115 { 4116 pragma(inline, true); 4117 return containsAt(_root, key); 4118 } 4119 } 4120 else 4121 { 4122 /** Returns: `true` if `key` is stored, `false` otherwise. */ 4123 bool contains(UKey key) const 4124 { 4125 pragma(inline, true); 4126 return containsAt(_root, key); 4127 } 4128 } 4129 4130 /** Insert `key` into `this` tree. */ 4131 static if (isValue) 4132 { 4133 Node insert(UKey key, in Value value, out ElementRef elementRef) 4134 { 4135 version(LDC) pragma(inline, true); 4136 return _root = insertAt(_root, Elt!Value(key, value), elementRef); 4137 } 4138 } 4139 else 4140 { 4141 Node insert(UKey key, out ElementRef elementRef) 4142 { 4143 version(LDC) pragma(inline, true); 4144 return _root = insertAt(_root, key, elementRef); 4145 } 4146 } 4147 4148 size_t countHeapNodes() 4149 { 4150 pragma(inline, true); 4151 return countHeapNodesAt(_root); 4152 } 4153 4154 /** Returns: `true` iff tree is empty (no elements stored). */ 4155 bool empty() const 4156 { 4157 pragma(inline, true); 4158 return !_root; 4159 } 4160 4161 /** Returns: number of elements stored. */ 4162 size_t length() const 4163 { 4164 pragma(inline, true); 4165 return _length; 4166 } 4167 4168 Node rootNode() const 4169 { 4170 pragma(inline, true); 4171 return _root; 4172 } 4173 4174 private: 4175 /** Returns: number of nodes used in `this` tree. Should always equal `Stats.heapNodeCount`. */ 4176 // debug size_t heapAllocationBalance() { return _heapAllocBalance; } 4177 } 4178 4179 void print() @safe const 4180 { 4181 printAt(_root, 0); 4182 } 4183 4184 Node root() 4185 { 4186 pragma(inline, true); 4187 return _root; 4188 } 4189 4190 private: 4191 Node _root; 4192 size_t _length; /// Number of elements (keys or key-value-pairs) currently stored under `_root` 4193 } 4194 } 4195 4196 /** Append statistics of tree under `Node` `curr.` into `stats`. 4197 */ 4198 static private void calculate(Value)(RawRadixTree!(Value).NodeType curr, 4199 ref RawRadixTree!(Value).StatsType stats) 4200 @safe pure nothrow /* TODO @nogc */ 4201 { 4202 alias RT = RawRadixTree!(Value); 4203 ++stats.popByNodeType[curr.typeIx]; 4204 4205 final switch (curr.typeIx) with (RT.NodeType.Ix) 4206 { 4207 case undefined: break; 4208 case ix_OneLeafMax7: break; // TODO calculate() 4209 case ix_TwoLeaf3: break; // TODO calculate() 4210 case ix_TriLeaf2: break; // TODO calculate() 4211 case ix_HeptLeaf1: break; // TODO calculate() 4212 case ix_SparseLeaf1Ptr: 4213 ++stats.heapNodeCount; 4214 const curr_ = curr.as!(SparseLeaf1!Value*); 4215 assert(curr_.length); 4216 ++stats.popHist_SparseLeaf1[curr_.length - 1]; // TODO type-safe indexing 4217 stats.sparseLeaf1AllocatedSizeSum += curr_.allocatedSize; 4218 break; 4219 case ix_DenseLeaf1Ptr: 4220 const curr_ = curr.as!(DenseLeaf1!Value*); 4221 ++stats.heapNodeCount; 4222 immutable count = curr_._ixBits.countOnes; // number of non-zero sub-nodes 4223 assert(count <= curr_.capacity); 4224 ++stats.popHist_DenseLeaf1[count - 1]; // TODO type-safe indexing 4225 break; 4226 case ix_SparseBranchPtr: 4227 ++stats.heapNodeCount; 4228 curr.as!(RT.SparseBranchType*).calculate(stats); 4229 break; 4230 case ix_DenseBranchPtr: 4231 ++stats.heapNodeCount; 4232 curr.as!(RT.DenseBranchType*).calculate(stats); 4233 break; 4234 } 4235 } 4236 4237 /** Append statistics of tree under `Leaf1!Value` `curr.` into `stats`. 4238 */ 4239 static private void calculate(Value)(Leaf1!Value curr, 4240 ref RawRadixTree!(Value).StatsType stats) 4241 @safe pure nothrow /* TODO @nogc */ 4242 { 4243 alias RT = RawRadixTree!(Value); 4244 ++stats.popByLeaf1Type[curr.typeIx]; 4245 4246 final switch (curr.typeIx) with (Leaf1!Value.Ix) 4247 { 4248 case undefined: break; 4249 case ix_HeptLeaf1: break; // TODO calculate() 4250 case ix_SparseLeaf1Ptr: 4251 ++stats.heapNodeCount; 4252 const curr_ = curr.as!(SparseLeaf1!Value*); 4253 assert(curr_.length); 4254 ++stats.popHist_SparseLeaf1[curr_.length - 1]; // TODO type-safe indexing 4255 break; 4256 case ix_DenseLeaf1Ptr: 4257 const curr_ = curr.as!(DenseLeaf1!Value*); 4258 ++stats.heapNodeCount; 4259 immutable count = curr_._ixBits.countOnes; // number of non-zero curr-nodes 4260 assert(count <= curr_.capacity); 4261 assert(count); 4262 ++stats.popHist_DenseLeaf1[count - 1]; // TODO type-safe indexing 4263 break; 4264 } 4265 } 4266 4267 /** Remap fixed-length typed key `typedKey` to raw (untyped) key of type `UKey`. 4268 TODO DIP-1000 scope 4269 */ 4270 UKey toFixedRawKey(TypedKey)(const scope TypedKey typedKey, UKey preallocatedFixedUKey) @trusted 4271 { 4272 enum radix = 2^^span; // branch-multiplicity, typically either 2, 4, 16 or 256 4273 immutable key_ = typedKey.bijectToUnsigned; 4274 4275 static assert(key_.sizeof == TypedKey.sizeof); 4276 4277 enum nbits = 8*key_.sizeof; // number of bits in key 4278 enum chunkCount = nbits/span; // number of chunks in key_ 4279 static assert(chunkCount*span == nbits, "Bitsize of TypedKey must be a multiple of span:" ~ span.stringof); 4280 4281 // big-endian storage 4282 foreach (immutable i; 0 .. chunkCount) // for each chunk index 4283 { 4284 immutable bitShift = (chunkCount - 1 - i)*span; // most significant bit chunk first (MSBCF) 4285 preallocatedFixedUKey[i] = (key_ >> bitShift) & (radix - 1); // part of value which is also an index 4286 } 4287 4288 return preallocatedFixedUKey[]; 4289 } 4290 4291 /** Remap typed key `typedKey` to raw (untyped) key of type `UKey`. 4292 */ 4293 UKey toRawKey(TypedKey)(in TypedKey typedKey, ref Array!Ix rawUKey) @trusted 4294 if (isTrieableKeyType!TypedKey) 4295 { 4296 enum radix = 2^^span; // branch-multiplicity, typically either 2, 4, 16 or 256 4297 4298 import nxt.container_traits : isAddress; 4299 static if (isAddress!TypedKey) 4300 { 4301 static assert(0, "Shift TypedKey " ~ TypedKey.stringof ~ " down by its alignment before returning"); 4302 } 4303 4304 static if (isFixedTrieableKeyType!TypedKey) 4305 { 4306 rawUKey.length = TypedKey.sizeof; 4307 return typedKey.toFixedRawKey(rawUKey[]); 4308 } 4309 else static if (isArray!TypedKey) 4310 { 4311 alias EType = Unqual!(typeof(TypedKey.init[0])); 4312 static if (is(EType == char)) // TODO extend to support isTrieableKeyType!TypedKey 4313 { 4314 import std.string : representation; 4315 const ubyte[] ukey = typedKey.representation; // lexical byte-order 4316 return cast(Ix[])ukey; // TODO needed? 4317 } 4318 else static if (is(EType == wchar)) 4319 { 4320 immutable ushort[] rKey = typedKey.representation; // lexical byte-order. 4321 // TODO MSByte-order of elements in rKey for ordered access and good branching performance 4322 immutable ubyte[] ukey = (cast(const ubyte*)rKey.ptr)[0 .. rKey[0].sizeof * rKey.length]; // TODO @trusted functionize. Reuse existing Phobos function? 4323 return ukey; 4324 } 4325 else static if (is(EType == dchar)) 4326 { 4327 immutable uint[] rKey = typedKey.representation; // lexical byte-order 4328 // TODO MSByte-order of elements in rKey for ordered access and good branching performance 4329 immutable ubyte[] ukey = (cast(const ubyte*)rKey.ptr)[0 .. rKey[0].sizeof * rKey.length]; // TODO @trusted functionize. Reuse existing Phobos function? 4330 return ukey; 4331 } 4332 else static if (isFixedTrieableKeyType!E) 4333 { 4334 static assert(false, "TODO Convert array of typed fixed keys"); 4335 } 4336 else 4337 { 4338 static assert(false, "TODO Handle typed key " ~ TypedKey.stringof); 4339 } 4340 } 4341 else static if (is(TypedKey == struct)) 4342 { 4343 static if (TypedKey.tupleof.length == 1) // TypedKey is a wrapper type 4344 { 4345 return typedKey.tupleof[0].toRawKey(rawUKey); 4346 } 4347 else 4348 { 4349 enum members = __traits(allMembers, TypedKey); 4350 foreach (immutable i, immutable memberName; members) // for each member name in `struct TypedKey` 4351 { 4352 immutable member = __traits(getMember, typedKey, memberName); // member 4353 alias MemberType = typeof(member); 4354 4355 static if (i + 1 == members.length) // last member is allowed to be an array of fixed length 4356 { 4357 Array!Ix memberRawUKey; 4358 const memberRawKey = member.toRawKey(memberRawUKey); // TODO DIP-1000 scope 4359 rawUKey ~= memberRawUKey; 4360 } 4361 else // non-last member must be fixed 4362 { 4363 static assert(isFixedTrieableKeyType!MemberType, 4364 "Non-last " ~ i.stringof ~ ":th member of type " ~ MemberType.stringof ~ " must be of fixed length"); 4365 Ix[MemberType.sizeof] memberRawUKey; 4366 const memberRawKey = member.toFixedRawKey(memberRawUKey); // TODO DIP-1000 scope 4367 rawUKey ~= memberRawUKey[]; 4368 } 4369 } 4370 return rawUKey[]; // TODO return immutable slice 4371 } 4372 } 4373 else 4374 { 4375 static assert(false, "TODO Handle typed key " ~ TypedKey.stringof); 4376 } 4377 } 4378 4379 /** Remap raw untyped key `ukey` to typed key of type `TypedKey`. */ 4380 inout(TypedKey) toTypedKey(TypedKey)(inout(Ix)[] ukey) @trusted 4381 if (isTrieableKeyType!TypedKey) 4382 { 4383 import nxt.container_traits : isAddress; 4384 static if (isAddress!TypedKey) 4385 { 4386 static assert(0, "Shift TypedKey " ~ TypedKey.stringof ~ " up by its alignment before returning"); 4387 } 4388 4389 static if (isFixedTrieableKeyType!TypedKey) 4390 { 4391 enum nbits = 8*TypedKey.sizeof; // number of bits in key 4392 enum chunkCount = nbits/span; // number of chunks in key_ 4393 static assert(chunkCount*span == nbits, "Bitsize of TypedKey must be a multiple of span:" ~ span.stringof); 4394 4395 // TODO reuse existing trait UnsignedOf!TypedKey 4396 static if (TypedKey.sizeof == 1) { alias RawKey = ubyte; } 4397 else static if (TypedKey.sizeof == 2) { alias RawKey = ushort; } 4398 else static if (TypedKey.sizeof == 4) { alias RawKey = uint; } 4399 else static if (TypedKey.sizeof == 8) { alias RawKey = ulong; } 4400 4401 RawKey bKey = 0; 4402 4403 // big-endian storage 4404 foreach (immutable i; 0 .. chunkCount) // for each chunk index 4405 { 4406 immutable RawKey uix = cast(RawKey)ukey[i]; 4407 immutable bitShift = (chunkCount - 1 - i)*span; // most significant bit chunk first (MSBCF) 4408 bKey |= uix << bitShift; // part of value which is also an index 4409 } 4410 4411 TypedKey typedKey; 4412 bKey.bijectFromUnsigned(typedKey); 4413 return typedKey; 4414 } 4415 else static if (isArray!TypedKey) 4416 { 4417 static if (isArray!TypedKey && 4418 is(Unqual!(typeof(TypedKey.init[0])) == char)) 4419 { 4420 static assert(char.sizeof == Ix.sizeof); 4421 return cast(inout(char)[])ukey; 4422 } 4423 // TODO handle wchar and dchar 4424 else 4425 { 4426 static assert(false, "TODO Handle typed key " ~ TypedKey.stringof); 4427 } 4428 } 4429 else static if (is(TypedKey == struct)) 4430 { 4431 static if (TypedKey.tupleof.length == 1) // TypedKey is a wrapper type 4432 { 4433 alias WrappedTypedKey = typeof(TypedKey.tupleof[0]); 4434 return TypedKey(ukey.toTypedKey!(WrappedTypedKey)); 4435 } 4436 else 4437 { 4438 TypedKey typedKey; 4439 size_t ix = 0; 4440 enum members = __traits(allMembers, TypedKey); 4441 foreach (immutable i, immutable memberName; members) // for each member name in `struct TypedKey` 4442 { 4443 alias MemberType = typeof(__traits(getMember, typedKey, memberName)); 4444 4445 static if (i + 1 != members.length) // last member is allowed to be an array of fixed length 4446 { 4447 static assert(isFixedTrieableKeyType!MemberType, 4448 "Non-last MemberType must be fixed length"); 4449 } 4450 __traits(getMember, typedKey, memberName) = ukey[ix .. ix + MemberType.sizeof].toTypedKey!MemberType; 4451 ix += MemberType.sizeof; 4452 } 4453 return typedKey; 4454 } 4455 } 4456 else 4457 { 4458 static assert(false, "TODO Handle typed key " ~ TypedKey.stringof); 4459 } 4460 } 4461 4462 /** Radix-Tree with key of type `K` and value of type `V` (if non-`void`). */ 4463 struct RadixTree(K, V) 4464 if (isTrieableKeyType!(K)) 4465 { 4466 alias RawTree = RawRadixTree!(V); 4467 4468 alias KeyType = K; 4469 alias ValueType = V; 4470 4471 static if (RawTree.hasValue) 4472 { 4473 /** Element type with typed key and value. */ 4474 struct TypedElt 4475 { 4476 K key; 4477 V value; 4478 } 4479 4480 ref V opIndex(K key) 4481 { 4482 pragma(inline, true); 4483 V* value = contains(key); 4484 version(assert) 4485 { 4486 import core.exception : RangeError; 4487 if (value is null) { throw new RangeError("Range violation"); } 4488 } 4489 return *value; 4490 } 4491 4492 auto opIndexAssign(in V value, K key) 4493 { 4494 _rawTree.ElementRefType elementRef; // reference to where element was added 4495 4496 Array!Ix rawUKey; 4497 auto rawKey = key.toRawKey(rawUKey); // TODO DIP-1000 scope 4498 4499 _rawTree.insert(rawKey, value, elementRef); 4500 4501 immutable bool added = elementRef.node && elementRef.modStatus == ModStatus.added; 4502 _length += added; 4503 /* TODO return reference (via `auto ref` return typed) to stored 4504 value at `elementRef` instead, unless packed static_bitarray storage is used 4505 when `V is bool` */ 4506 return value; 4507 } 4508 4509 /** Insert `key`. 4510 Returns: `true` if `key` wasn't previously added, `false` otherwise. 4511 */ 4512 bool insert(in K key, in V value) @nogc 4513 { 4514 _rawTree.ElementRefType elementRef; // indicates that key was added 4515 4516 Array!Ix rawUKey; 4517 auto rawKey = key.toRawKey(rawUKey); // TODO DIP-1000 scope 4518 4519 _rawTree.insert(rawKey, value, elementRef); 4520 4521 // debug if (willFail) { dbg("WILL FAIL: elementRef:", elementRef, " key:", key); } 4522 if (elementRef.node) // if `key` was added at `elementRef` 4523 { 4524 // set value 4525 final switch (elementRef.node.typeIx) with (_rawTree.NodeType.Ix) 4526 { 4527 case undefined: 4528 case ix_OneLeafMax7: 4529 case ix_TwoLeaf3: 4530 case ix_TriLeaf2: 4531 case ix_HeptLeaf1: 4532 case ix_SparseBranchPtr: 4533 case ix_DenseBranchPtr: 4534 assert(false); 4535 case ix_SparseLeaf1Ptr: 4536 elementRef.node.as!(SparseLeaf1!V*).setValue(UIx(rawKey[$ - 1]), value); 4537 break; 4538 case ix_DenseLeaf1Ptr: 4539 elementRef.node.as!(DenseLeaf1!V*).setValue(UIx(rawKey[$ - 1]), value); 4540 break; 4541 } 4542 immutable bool hit = elementRef.modStatus == ModStatus.added; 4543 _length += hit; 4544 return hit; 4545 } 4546 else 4547 { 4548 assert(false, "TODO warning no elementRef for key:"/*, key, " rawKey:", rawKey*/); 4549 } 4550 } 4551 4552 /** Returns: pointer to value if `key` is contained in set, null otherwise. */ 4553 inout(V*) contains(in K key) inout @nogc 4554 { 4555 version(LDC) pragma(inline, true); 4556 Array!Ix rawUKey; 4557 auto rawKey = key.toRawKey(rawUKey); // TODO DIP-1000 scope 4558 return _rawTree.contains(rawKey); 4559 } 4560 4561 /** AA-style key-value range. */ 4562 Range byKeyValue() @nogc // TODO inout?, TODO DIP-1000 scope 4563 { 4564 pragma(inline, true); 4565 return this.opSlice; 4566 } 4567 } 4568 else 4569 { 4570 @nogc: 4571 alias TypedElt = K; 4572 4573 /** Insert `key`. 4574 Returns: `true` if `key` wasn't previously added, `false` otherwise. 4575 */ 4576 bool insert(K key) 4577 { 4578 _rawTree.ElementRefType elementRef; // indicates that elt was added 4579 4580 Array!Ix rawUKey; 4581 auto rawKey = key.toRawKey(rawUKey); // TODO DIP-1000 scope 4582 4583 _rawTree.insert(rawKey, elementRef); 4584 4585 immutable bool hit = elementRef.node && elementRef.modStatus == ModStatus.added; 4586 _length += hit; 4587 return hit; 4588 } 4589 4590 nothrow: 4591 4592 /** Returns: `true` if `key` is stored, `false` otherwise. */ 4593 bool contains(in K key) inout 4594 { 4595 version(LDC) pragma(inline, true); 4596 Array!Ix rawUKey; 4597 auto rawKey = key.toRawKey(rawUKey); // TODO DIP-1000 scope 4598 return _rawTree.contains(rawKey); 4599 } 4600 4601 /** AA-style key range. */ 4602 Range byKey() @nogc // TODO inout?. TODO DIP-1000 scope 4603 { 4604 pragma(inline, true); 4605 return this.opSlice; 4606 } 4607 } 4608 4609 /** Supports $(B `K` in `this`) syntax. */ 4610 auto opBinaryRight(string op)(in K key) inout 4611 if (op == "in") 4612 { 4613 pragma(inline, true); 4614 return contains(key); // TODO return `_rawTree.ElementRefType` 4615 } 4616 4617 Range opSlice() @system @nogc // TODO inout? 4618 { 4619 version(LDC) pragma(inline, true); 4620 return Range(_root, []); 4621 } 4622 4623 /** Get range over elements whose key starts with `keyPrefix`. 4624 The element equal to `keyPrefix` is return as an empty instance of the type. 4625 */ 4626 auto prefix(K keyPrefix) @system 4627 { 4628 Array!Ix rawUKey; 4629 auto rawKeyPrefix = keyPrefix.toRawKey(rawUKey); 4630 4631 UKey rawKeyPrefixRest; 4632 auto prefixedRootNode = _rawTree.prefix(rawKeyPrefix, rawKeyPrefixRest); 4633 4634 return Range(prefixedRootNode, 4635 rawKeyPrefixRest); 4636 } 4637 4638 /** Typed Range. */ 4639 private static struct Range 4640 { 4641 @nogc: 4642 4643 this(RawTree.NodeType root, 4644 UKey keyPrefixRest) 4645 { 4646 pragma(inline, true); 4647 _rawRange = _rawTree.RangeType(root, keyPrefixRest); 4648 } 4649 4650 TypedElt front() const 4651 { 4652 pragma(inline, true); 4653 static if (RawTree.hasValue) 4654 { 4655 return typeof(return)(_rawRange.lowKey.toTypedKey!K, 4656 _rawRange._front._cachedFrontValue); 4657 } 4658 else 4659 { 4660 return _rawRange.lowKey.toTypedKey!K; 4661 } 4662 } 4663 4664 TypedElt back() const 4665 { 4666 pragma(inline, true); 4667 static if (RawTree.hasValue) 4668 { 4669 return typeof(return)(_rawRange.highKey.toTypedKey!K, 4670 _rawRange._back._cachedFrontValue); 4671 } 4672 else 4673 { 4674 return _rawRange.highKey.toTypedKey!K; 4675 } 4676 } 4677 4678 @property typeof(this) save() 4679 { 4680 version(LDC) pragma(inline, true); 4681 typeof(return) copy; 4682 copy._rawRange = this._rawRange.save; 4683 return copy; 4684 } 4685 4686 RawTree.RangeType _rawRange; 4687 alias _rawRange this; 4688 } 4689 4690 /** Raw Range. */ 4691 private static struct RawRange 4692 { 4693 this(RawTree.NodeType root, 4694 UKey keyPrefixRest) 4695 { 4696 pragma(inline, true); 4697 this._rawRange = _rawTree.RangeType(root, keyPrefixRest); 4698 } 4699 4700 static if (RawTree.hasValue) 4701 { 4702 const(Elt!V) front() const 4703 { 4704 pragma(inline, true); 4705 return typeof(return)(_rawRange.lowKey, 4706 _rawRange._front._cachedFrontValue); 4707 } 4708 4709 const(Elt!V) back() const 4710 { 4711 pragma(inline, true); 4712 return typeof(return)(_rawRange.highKey, 4713 _rawRange._back._cachedFrontValue); 4714 } 4715 } 4716 else 4717 { 4718 const(Elt!V) front() const 4719 { 4720 pragma(inline, true); 4721 return _rawRange.lowKey; 4722 } 4723 const(Elt!V) back() const 4724 { 4725 pragma(inline, true); 4726 return _rawRange.highKey; 4727 } 4728 } 4729 4730 @property typeof(this) save() 4731 { 4732 pragma(inline, true); 4733 typeof(return) copy; 4734 copy._rawRange = this._rawRange.save; 4735 return copy; 4736 } 4737 4738 RawTree.RangeType _rawRange; 4739 alias _rawRange this; 4740 } 4741 4742 /** This function searches with policy `sp` to find the largest right subrange 4743 on which pred(value, x) is true for all x (e.g., if pred is "less than", 4744 returns the portion of the range with elements strictly greater than 4745 value). 4746 TODO Add template param (SearchPolicy sp) 4747 4748 TODO replace `matchCommonPrefix` with something more clever directly 4749 finds the next element after rawKey and returns a TreeRange at that point 4750 */ 4751 auto upperBound(K key) @system 4752 { 4753 Array!Ix rawUKey; 4754 auto rawKey = key.toRawKey(rawUKey); 4755 4756 UKey rawKeyRest; 4757 auto prefixedRootNode = _rawTree.matchCommonPrefix(rawKey, rawKeyRest); 4758 4759 return UpperBoundRange(prefixedRootNode, 4760 rawKey[0 .. $ - rawKeyRest.length], 4761 rawKeyRest); 4762 } 4763 4764 /** Typed Upper Bound Range. 4765 */ 4766 private static struct UpperBoundRange 4767 { 4768 @nogc: 4769 4770 this(RawTree.NodeType root, 4771 UKey rawKeyPrefix, UKey rawKeyRest) 4772 { 4773 _rawRange = _rawTree.RangeType(root, []); 4774 _rawKeyPrefix = rawKeyPrefix; 4775 4776 // skip values 4777 import std.algorithm.comparison : cmp; 4778 while (!_rawRange.empty && 4779 cmp(_rawRange._front.frontKey, rawKeyRest) <= 0) 4780 { 4781 _rawRange.popFront(); 4782 } 4783 } 4784 4785 TypedElt front() 4786 { 4787 Array!Ix wholeRawKey = _rawKeyPrefix.dup; 4788 wholeRawKey ~= _rawRange.lowKey; 4789 static if (RawTree.hasValue) 4790 { 4791 return typeof(return)(wholeRawKey[].toTypedKey!K, 4792 _rawRange._front._cachedFrontValue); 4793 } 4794 else 4795 { 4796 return wholeRawKey[].toTypedKey!K; 4797 } 4798 } 4799 4800 @property typeof(this) save() 4801 { 4802 pragma(inline, true); 4803 typeof(return) copy; 4804 copy._rawRange = this._rawRange.save; 4805 copy._rawKeyPrefix = this._rawKeyPrefix.dup; 4806 return copy; 4807 } 4808 4809 RawTree.RangeType _rawRange; 4810 alias _rawRange this; 4811 Array!Ix _rawKeyPrefix; 4812 } 4813 4814 static if (RawTree.hasValue) 4815 { 4816 import std.algorithm.iteration : map; 4817 4818 // /** Get range of map keys. */ 4819 // auto byKey() 4820 // { 4821 // return this[].map!(e => e[0]); 4822 // } 4823 4824 // /** Get range of map values. */ 4825 // auto byValue() 4826 // { 4827 // return this[].map!(e => e[1]); 4828 // } 4829 } 4830 4831 /** Returns a duplicate of this tree. 4832 Shallowly duplicates the values in the map case. 4833 */ 4834 typeof(this) dup() 4835 { 4836 return typeof(this)(this._rawTree.dup); 4837 } 4838 4839 RawTree _rawTree; 4840 alias _rawTree this; 4841 } 4842 alias PatriciaTrie = RadixTree; 4843 alias RadixTrie = RadixTree; 4844 alias CompactPrefixTree = RadixTree; 4845 4846 template RadixTreeSet(K) 4847 if (isTrieableKeyType!(K)) 4848 { 4849 alias RadixTreeSet = RadixTree!(K, void); 4850 } 4851 4852 /** Print `tree`. */ 4853 void print(Key, Value)(const ref RadixTree!(Key, Value) tree) @safe 4854 if (isTrieableKeyType!(K)) 4855 { 4856 print(tree._rawTree); 4857 } 4858 4859 /** Keys are stored in a way that they can't be accessed by reference so we 4860 allow array (and string) keys to be of mutable type. 4861 */ 4862 template MutableKey(Key) 4863 { 4864 static if (isArray!Key) 4865 { 4866 alias MutableKey = const(Unqual!(typeof(Key.init[0])))[]; 4867 } 4868 else 4869 { 4870 alias MutableKey = Key; 4871 } 4872 } 4873 4874 /** Instantiator for the set-version of `RadixTree` where value-type is `void` (unused). */ 4875 RadixTree!(MutableKey!Key, void) radixTreeSet(Key)() @nogc 4876 { 4877 return typeof(return)(); 4878 } 4879 4880 /** Instantiator for the map-version of `RadixTree` where value-type is `Value`. */ 4881 RadixTree!(MutableKey!Key, Value) radixTreeMap(Key, Value)() 4882 { 4883 return typeof(return)(); 4884 } 4885 4886 @safe pure nothrow @nogc unittest 4887 { version(showAssertTags) dbg(); 4888 version(enterSingleInfiniteMemoryLeakTest) 4889 { 4890 while (true) 4891 { 4892 checkNumeric!(bool, float, double, 4893 long, int, short, byte, 4894 ulong, uint, ushort, ubyte); 4895 } 4896 } 4897 else 4898 { 4899 checkNumeric!(bool, float, double, 4900 long, int, short, byte, 4901 ulong, uint, ushort, ubyte); 4902 } 4903 } 4904 4905 /// exercise all switch-cases in `RawRadixTree.prefixAt()` 4906 /*TODO @safe*/ pure nothrow 4907 /*TODO:@nogc*/ unittest 4908 { version(showAssertTags) dbg(); 4909 import std.algorithm.comparison : equal; 4910 alias Key = string; 4911 auto set = radixTreeSet!(Key); 4912 4913 set.clear(); 4914 set.insert(`-----1`); 4915 assert(set.prefix(`-----`).equal([`1`])); 4916 assert(set.prefix(`-----_`).empty); 4917 assert(set.prefix(`-----____`).empty); 4918 set.insert(`-----2`); 4919 assert(set.prefix(`-----`).equal([`1`, `2`])); 4920 assert(set.prefix(`-----_`).empty); 4921 assert(set.prefix(`-----____`).empty); 4922 set.insert(`-----3`); 4923 assert(set.prefix(`-----`).equal([`1`, `2`, `3`])); 4924 assert(set.prefix(`-----_`).empty); 4925 assert(set.prefix(`-----____`).empty); 4926 set.insert(`-----4`); 4927 assert(set.prefix(`-----`).equal([`1`, `2`, `3`, `4`])); 4928 assert(set.prefix(`-----_`).empty); 4929 assert(set.prefix(`-----____`).empty); 4930 set.insert(`-----5`); 4931 assert(set.prefix(`-----`).equal([`1`, `2`, `3`, `4`, `5`])); 4932 assert(set.prefix(`-----_`).empty); 4933 assert(set.prefix(`-----____`).empty); 4934 set.insert(`-----6`); 4935 assert(set.prefix(`-----`).equal([`1`, `2`, `3`, `4`, `5`, `6`])); 4936 assert(set.prefix(`-----_`).empty); 4937 assert(set.prefix(`-----____`).empty); 4938 set.insert(`-----7`); 4939 assert(set.prefix(`-----`).equal([`1`, `2`, `3`, `4`, `5`, `6`, `7`])); 4940 assert(set.prefix(`-----_`).empty); 4941 assert(set.prefix(`-----____`).empty); 4942 set.insert(`-----8`); 4943 assert(set.prefix(`-----`).equal([`1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`])); 4944 assert(set.prefix(`-----_`).empty); 4945 assert(set.prefix(`-----____`).empty); 4946 set.insert(`-----11`); 4947 assert(set.prefix(`-----`).equal([`1`, `11`, `2`, `3`, `4`, `5`, `6`, `7`, `8`])); 4948 set.insert(`-----22`); 4949 assert(set.prefix(`-----`).equal([`1`, `11`, `2`, `22`, `3`, `4`, `5`, `6`, `7`, `8`])); 4950 set.insert(`-----33`); 4951 assert(set.prefix(`-----`).equal([`1`, `11`, `2`, `22`, `3`, `33`, `4`, `5`, `6`, `7`, `8`])); 4952 set.insert(`-----44`); 4953 assert(set.prefix(`-----`).equal([`1`, `11`, `2`, `22`, `3`, `33`, `4`, `44`, `5`, `6`, `7`, `8`])); 4954 4955 set.clear(); 4956 set.insert(`-----11`); 4957 assert(set.prefix(`-----`).equal([`11`])); 4958 set.insert(`-----22`); 4959 assert(set.prefix(`-----`).equal([`11`, `22`])); 4960 assert(set.prefix(`-----_`).empty); 4961 assert(set.prefix(`-----___`).empty); 4962 4963 set.clear(); 4964 set.insert(`-----111`); 4965 assert(set.prefix(`-----`).equal([`111`])); 4966 set.insert(`-----122`); 4967 assert(set.prefix(`-----`).equal([`111`, `122`])); 4968 set.insert(`-----133`); 4969 assert(set.prefix(`-----`).equal([`111`, `122`, `133`])); 4970 assert(set.prefix(`-----1`).equal([`11`, `22`, `33`])); 4971 assert(set.prefix(`-----1_`).empty); 4972 assert(set.prefix(`-----1___`).empty); 4973 4974 set.clear(); 4975 set.insert(`-----1111`); 4976 assert(set.prefix(`-----`).equal([`1111`])); 4977 assert(set.prefix(`-----_`).empty); 4978 assert(set.prefix(`-----___`).empty); 4979 4980 set.clear(); 4981 set.insert(`-----11111`); 4982 assert(set.prefix(`-----`).equal([`11111`])); 4983 assert(set.prefix(`-----_`).empty); 4984 assert(set.prefix(`-----___`).empty); 4985 set.insert(`-----12222`); 4986 assert(set.prefix(`-----`).equal([`11111`, `12222`])); 4987 assert(set.prefix(`-----_`).empty); 4988 assert(set.prefix(`-----___`).empty); 4989 assert(set.prefix(`-----12`).equal([`222`])); 4990 assert(set.prefix(`-----12_`).empty); 4991 assert(set.prefix(`-----12____`).empty); 4992 } 4993 4994 /// test floating-point key range sortedness 4995 /*@ TODO safe */ pure nothrow @nogc unittest 4996 { version(showAssertTags) dbg(); 4997 alias T = double; 4998 4999 auto set = radixTreeSet!(T); 5000 5001 import std.range: isForwardRange; 5002 static assert(isForwardRange!(typeof(set[]))); 5003 5004 set.insert(-1.1e6); 5005 set.insert(-2.2e9); 5006 set.insert(-1.1); 5007 set.insert(+2.2); 5008 set.insert(T.max); 5009 set.insert(-T.max); 5010 set.insert(-3.3); 5011 set.insert(-4.4); 5012 set.insert(+4.4); 5013 set.insert(+3.3); 5014 set.insert(+1.1e6); 5015 set.insert(+2.2e9); 5016 5017 import std.algorithm.sorting : isSorted; 5018 assert(set[].isSorted); 5019 } 5020 5021 version(unittest) 5022 auto testScalar(uint span, Keys...)() @safe 5023 if (Keys.length != 0) 5024 { 5025 import std.traits : isIntegral, isFloatingPoint; 5026 import std.range : iota; 5027 foreach (Key; Keys) 5028 { 5029 auto set = radixTreeSet!(Key); 5030 5031 static if (isIntegral!Key) 5032 { 5033 immutable low = max(Key.min, -1_000); 5034 immutable high = min(Key.max, 1_000); 5035 immutable factor = 1; 5036 } 5037 else static if (isFloatingPoint!Key) 5038 { 5039 immutable low = -1_000; 5040 immutable high = 1_000; 5041 immutable factor = 1; 5042 } 5043 else static if (is(Key == bool)) 5044 { 5045 immutable low = false; 5046 immutable high = true; 5047 immutable factor = 1; 5048 } 5049 5050 foreach (immutable uk_; low.iota(high + 1)) 5051 { 5052 immutable uk = factor*uk_; 5053 immutable Key key = cast(Key)uk; 5054 assert(set.insert(key)); // insert new value returns `true` (previously not in set) 5055 assert(!set.insert(key)); // reinsert same value returns `false` (already in set) 5056 } 5057 5058 import std.algorithm.comparison : equal; 5059 import std.algorithm.iteration : map; 5060 () @trusted { assert(set[].equal(low.iota(high + 1).map!(uk => cast(Key)uk))); } (); // TODO remove @trusted when opSlice support DIP-1000 5061 5062 import std.algorithm.sorting : isSorted; 5063 () @trusted { assert(set[].isSorted); } (); // TODO remove @trusted when opSlice support DIP-1000 5064 } 5065 } 5066 5067 /// 5068 @safe pure nothrow @nogc unittest 5069 { version(showAssertTags) dbg(); 5070 testScalar!(8, 5071 bool, 5072 double, float, 5073 long, int, short, byte, 5074 ulong, uint, ushort, ubyte); 5075 } 5076 5077 /// 5078 @safe pure nothrow @nogc unittest 5079 { version(showAssertTags) dbg(); 5080 alias Key = ubyte; 5081 auto set = radixTreeSet!(Key); 5082 5083 assert(!set.root); 5084 5085 foreach (immutable i; 0 .. 7) 5086 { 5087 assert(!set.contains(i)); 5088 assert(set.insert(i)); 5089 assert(set.root.peek!HeptLeaf1); 5090 } 5091 5092 foreach (immutable i; 7 .. 48) 5093 { 5094 assert(!set.contains(i)); 5095 assert(set.insert(i)); 5096 assert(set.root.peek!(SparseLeaf1!void*)); 5097 } 5098 5099 foreach (immutable i; 48 .. 256) 5100 { 5101 assert(!set.contains(i)); 5102 assert(set.insert(i)); 5103 assert(set.root.peek!(DenseLeaf1!void*)); 5104 } 5105 } 5106 5107 /** Calculate and print statistics of `tree`. */ 5108 void showStatistics(RT)(const ref RT tree) // why does `in`RT tree` trigger a copy ctor here 5109 { 5110 import std.conv : to; 5111 import std.stdio : writeln; 5112 5113 auto stats = tree.usageHistograms; 5114 5115 writeln("Population By Node Type: ", stats.popByNodeType); 5116 writeln("Population By Leaf1 Type: ", stats.popByLeaf1Type); 5117 5118 writeln("SparseBranch Population Histogram: ", stats.popHist_SparseBranch); 5119 writeln("DenseBranch Population Histogram: ", stats.popHist_DenseBranch); 5120 5121 writeln("SparseLeaf1 Population Histogram: ", stats.popHist_SparseLeaf1); 5122 writeln("DenseLeaf1 Population Histogram: ", stats.popHist_DenseLeaf1); 5123 5124 size_t totalBytesUsed = 0; 5125 5126 // Node-usage 5127 foreach (const index, const pop; stats.popByNodeType) // TODO use stats.byPair when added to typecons_ex.d 5128 { 5129 size_t bytesUsed = 0; 5130 const ix = cast(RT.NodeType.Ix)index; 5131 final switch (ix) with (RT.NodeType.Ix) 5132 { 5133 case undefined: continue; // ignore 5134 case ix_OneLeafMax7: 5135 bytesUsed = pop*OneLeafMax7.sizeof; 5136 break; 5137 case ix_TwoLeaf3: 5138 bytesUsed = pop*TwoLeaf3.sizeof; 5139 break; 5140 case ix_TriLeaf2: 5141 bytesUsed = pop*TriLeaf2.sizeof; 5142 break; 5143 case ix_HeptLeaf1: 5144 bytesUsed = pop*HeptLeaf1.sizeof; 5145 break; 5146 case ix_SparseLeaf1Ptr: 5147 bytesUsed = stats.sparseLeaf1AllocatedSizeSum; // variable length struct 5148 totalBytesUsed += bytesUsed; 5149 break; 5150 case ix_DenseLeaf1Ptr: 5151 bytesUsed = pop*DenseLeaf1!(RT.ValueType).sizeof; 5152 totalBytesUsed += bytesUsed; 5153 break; 5154 case ix_SparseBranchPtr: 5155 bytesUsed = stats.sparseBranchAllocatedSizeSum; // variable length struct 5156 totalBytesUsed += bytesUsed; 5157 break; 5158 case ix_DenseBranchPtr: 5159 bytesUsed = pop*RT.DenseBranchType.sizeof; 5160 totalBytesUsed += bytesUsed; 5161 break; 5162 } 5163 writeln(pop, " number of ", 5164 ix.to!string[3 .. $], // TODO Use RT.NodeType.indexTypeName(ix) 5165 " uses ", bytesUsed/1e6, " megabytes"); 5166 } 5167 5168 writeln("Tree uses ", totalBytesUsed/1e6, " megabytes"); 5169 } 5170 5171 /// test map from `uint` to values of type `double` 5172 @safe pure nothrow @nogc unittest 5173 { version(showAssertTags) dbg(); 5174 alias Key = uint; 5175 alias Value = uint; 5176 5177 auto map = radixTreeMap!(Key, Value); 5178 assert(map.empty); 5179 5180 static assert(map.hasValue); 5181 5182 static Value keyToValue(Key key) @safe pure nothrow @nogc { return cast(Value)((key + 1)*radix); } 5183 5184 foreach (immutable i; 0 .. SparseLeaf1!Value.maxCapacity) 5185 { 5186 assert(!map.contains(i)); 5187 assert(map.length == i); 5188 map[i] = keyToValue(i); 5189 assert(map.contains(i)); 5190 assert(*map.contains(i) == keyToValue(i)); 5191 assert(i in map); 5192 assert(*(i in map) == keyToValue(i)); 5193 assert(map.length == i + 1); 5194 } 5195 5196 foreach (immutable i; SparseLeaf1!Value.maxCapacity .. radix) 5197 { 5198 assert(!map.contains(i)); 5199 assert(map.length == i); 5200 map[i] = keyToValue(i); 5201 assert(map.contains(i)); 5202 assert(*map.contains(i) == keyToValue(i)); 5203 assert(i in map); 5204 assert(*(i in map) == keyToValue(i)); 5205 assert(map.length == i + 1); 5206 } 5207 5208 void testRange() @trusted 5209 { 5210 size_t i = 0; 5211 foreach (immutable keyValue; map[]) 5212 { 5213 assert(keyValue.key == i); 5214 assert(keyValue.value == keyToValue(cast(Key)i)); // TODO use typed key instead of cast(Key) 5215 ++i; 5216 } 5217 } 5218 5219 testRange(); 5220 } 5221 5222 /** Check string types in `Keys`. */ 5223 auto testString(Keys...)(size_t count, uint maxLength) 5224 if (Keys.length != 0) 5225 { 5226 void testContainsAndInsert(Set, Key)(ref Set set, Key key) 5227 if (isSomeString!Key) 5228 { 5229 import std.conv : to; 5230 immutable failMessage = `Failed for key: "` ~ key.to!string ~ `"`; 5231 5232 assert(!set.contains(key), failMessage); 5233 5234 assert(set.insert(key), failMessage); 5235 assert(set.contains(key), failMessage); 5236 5237 assert(!set.insert(key), failMessage); 5238 assert(set.contains(key), failMessage); 5239 } 5240 5241 import std.algorithm.comparison : equal; 5242 import std.datetime.stopwatch : StopWatch, AutoStart; 5243 5244 foreach (Key; Keys) 5245 { 5246 auto set = radixTreeSet!(Key); 5247 assert(set.empty); 5248 5249 immutable sortedKeys = randomUniqueSortedStrings(count, maxLength); 5250 5251 auto sw1 = StopWatch(AutoStart.yes); 5252 foreach (immutable key; sortedKeys) 5253 { 5254 testContainsAndInsert(set, key); 5255 } 5256 sw1.stop; 5257 5258 version(show) 5259 { 5260 import std.conv : to; 5261 version(show) import std.stdio : writeln; 5262 version(show) writeln("Test for contains and insert took ", sw1.peek()); 5263 } 5264 5265 auto sw2 = StopWatch(AutoStart.yes); 5266 5267 void testPrefix() @trusted 5268 { 5269 assert(set[].equal(sortedKeys)); 5270 import std.algorithm.iteration : filter, map; 5271 assert(set.prefix("a") 5272 .equal(sortedKeys.filter!(x => x.length && x[0] == 'a') 5273 .map!(x => x[1 .. $]))); 5274 assert(set.prefix("aa") 5275 .equal(sortedKeys.filter!(x => x.length >= 2 && x[0] == 'a' && x[1] == 'a') 5276 .map!(x => x[2 .. $]))); 5277 } 5278 5279 testPrefix(); 5280 5281 sw2.stop; 5282 version(show) 5283 { 5284 import std.stdio : writeln; 5285 writeln("Compare took ", sw2.peek()); 5286 } 5287 } 5288 } 5289 5290 /// 5291 @safe /* TODO pure nothrow @nogc */ 5292 unittest 5293 { version(showAssertTags) dbg(); 5294 testString!(string)(512, 8); 5295 testString!(string)(512, 32); 5296 } 5297 5298 /// test map to values of type `bool` 5299 @safe pure nothrow @nogc unittest 5300 { version(showAssertTags) dbg(); 5301 alias Key = uint; 5302 alias Value = bool; 5303 5304 auto map = radixTreeMap!(Key, Value); 5305 assert(map.empty); 5306 5307 static assert(map.hasValue); 5308 map.insert(Key.init, Value.init); 5309 } 5310 5311 /// test packing of set elements 5312 @safe pure nothrow @nogc unittest 5313 { version(showAssertTags) dbg(); 5314 auto set = radixTreeSet!(ulong); 5315 enum N = HeptLeaf1.capacity; 5316 5317 foreach (immutable i; 0 .. N) 5318 { 5319 assert(!set.contains(i)); 5320 5321 assert(set.insert(i)); 5322 assert(set.contains(i)); 5323 5324 assert(!set.insert(i)); 5325 assert(set.contains(i)); 5326 } 5327 5328 foreach (immutable i; N .. 256) 5329 { 5330 assert(!set.contains(i)); 5331 5332 assert(set.insert(i)); 5333 assert(set.contains(i)); 5334 5335 assert(!set.insert(i)); 5336 assert(set.contains(i)); 5337 } 5338 5339 foreach (immutable i; 256 .. 256 + N) 5340 { 5341 assert(!set.contains(i)); 5342 5343 assert(set.insert(i)); 5344 assert(set.contains(i)); 5345 5346 assert(!set.insert(i)); 5347 assert(set.contains(i)); 5348 } 5349 5350 foreach (immutable i; 256 + N .. 256 + 256) 5351 { 5352 assert(!set.contains(i)); 5353 5354 assert(set.insert(i)); 5355 assert(set.contains(i)); 5356 5357 assert(!set.insert(i)); 5358 assert(set.contains(i)); 5359 } 5360 } 5361 5362 /// 5363 @safe pure nothrow @nogc unittest 5364 { version(showAssertTags) dbg(); 5365 auto set = radixTreeSet!(ubyte); 5366 alias Set = typeof(set); 5367 5368 foreach (immutable i; 0 .. HeptLeaf1.capacity) 5369 { 5370 assert(!set.contains(i)); 5371 5372 assert(set.insert(i)); 5373 assert(set.contains(i)); 5374 5375 assert(!set.insert(i)); 5376 assert(set.contains(i)); 5377 5378 immutable rootRef = set.root.peek!(HeptLeaf1); 5379 assert(rootRef); 5380 } 5381 5382 foreach (immutable i; HeptLeaf1.capacity .. 256) 5383 { 5384 assert(!set.contains(i)); 5385 5386 assert(set.insert(i)); 5387 assert(set.contains(i)); 5388 5389 assert(!set.insert(i)); 5390 assert(set.contains(i)); 5391 } 5392 } 5393 5394 /// 5395 @safe pure nothrow @nogc unittest 5396 { version(showAssertTags) dbg(); 5397 import std.meta : AliasSeq; 5398 foreach (T; AliasSeq!(ushort, uint)) 5399 { 5400 auto set = radixTreeSet!(T); 5401 alias Set = typeof(set); 5402 5403 foreach (immutable i; 0 .. 256) 5404 { 5405 assert(!set.contains(i)); 5406 5407 assert(set.insert(i)); 5408 assert(set.contains(i)); 5409 5410 assert(!set.insert(i)); 5411 assert(set.contains(i)); 5412 } 5413 5414 // 256 5415 assert(!set.contains(256)); 5416 5417 assert(set.insert(256)); 5418 assert(set.contains(256)); 5419 5420 assert(!set.insert(256)); 5421 assert(set.contains(256)); 5422 5423 // 257 5424 assert(!set.contains(257)); 5425 5426 assert(set.insert(257)); 5427 assert(set.contains(257)); 5428 5429 assert(!set.insert(257)); 5430 assert(set.contains(257)); 5431 5432 immutable rootRef = set.root.peek!(Set.DefaultBranchType*); 5433 assert(rootRef); 5434 immutable root = *rootRef; 5435 assert(root.prefix.length == T.sizeof - 2); 5436 5437 } 5438 } 5439 5440 /** Generate `count` number of random unique strings of minimum length 1 and 5441 maximum length of `maxLength`. 5442 */ 5443 private static auto randomUniqueSortedStrings(size_t count, uint maxLength) @trusted pure nothrow 5444 { 5445 import std.random : Random, uniform; 5446 auto gen = Random(); 5447 5448 // store set of unique keys using a builtin D associative array (AA) 5449 bool[string] stringSet; // set of strings using D's AA 5450 5451 try 5452 { 5453 while (stringSet.length < count) 5454 { 5455 const length = uniform(1, maxLength, gen); 5456 auto key = new char[length]; 5457 foreach (immutable ix; 0 .. length) 5458 { 5459 key[ix] = cast(char)('a' + 0.uniform(26, gen)); 5460 } 5461 stringSet[key[].idup] = true; 5462 } 5463 } 5464 catch (Exception e) 5465 { 5466 import nxt.dbgio : dbg; 5467 dbg("Couldn't randomize"); 5468 } 5469 5470 import std.array : array; 5471 import std.algorithm.sorting : sort; 5472 auto keys = stringSet.byKey.array; 5473 sort(keys); 5474 return keys; 5475 } 5476 5477 /** Create a set of words from the file `/usr/share/dict/words`. */ 5478 private void benchmarkReadDictWords(Value)(in size_t maxCount) 5479 { 5480 import std.range : chain; 5481 5482 const path = "/usr/share/dict/words"; 5483 5484 enum hasValue = !is(Value == void); 5485 static if (hasValue) 5486 { 5487 auto rtr = radixTreeMap!(string, Value); 5488 } 5489 else 5490 { 5491 auto rtr = radixTreeSet!(string); 5492 } 5493 assert(rtr.empty); 5494 5495 enum show = false; 5496 5497 string[] firsts = []; // used to test various things 5498 size_t count = 0; 5499 5500 import std.datetime.stopwatch : StopWatch, AutoStart; 5501 auto sw = StopWatch(AutoStart.yes); 5502 5503 import std.stdio : File; 5504 foreach (const word; chain(firsts, File(path).byLine)) 5505 { 5506 if (count >= maxCount) { break; } 5507 import nxt.array_algorithm : endsWith; 5508 import std.range.primitives : empty; 5509 if (!word.empty && 5510 !word.endsWith(`'s`)) // skip genitive forms 5511 { 5512 assert(!rtr.contains(word)); 5513 5514 static if (show) 5515 { 5516 import std.string : representation; 5517 // dbg(`word:"`, word, `" of length:`, word.length, 5518 // ` of representation:`, word.representation); 5519 // debug rtr.willFail = word == `amiable`; 5520 // if (rtr.willFail) 5521 // { 5522 // rtr.print(); 5523 // } 5524 } 5525 5526 static if (hasValue) 5527 { 5528 assert(rtr.insert(word, count)); 5529 const hitA = rtr.contains(word); 5530 assert(hitA); 5531 assert(*hitA == count); 5532 5533 assert(!rtr.insert(word, count)); 5534 const hitB = rtr.contains(word); 5535 assert(hitB); 5536 assert(*hitB == count); 5537 } 5538 else 5539 { 5540 assert(rtr.insert(word)); 5541 assert(rtr.contains(word)); 5542 5543 assert(!rtr.insert(word)); 5544 assert(rtr.contains(word)); 5545 } 5546 ++count; 5547 } 5548 } 5549 sw.stop; 5550 version(show) 5551 { 5552 import std.stdio : writeln; 5553 writeln("Added ", count, " words from ", path, " in ", sw.peek()); 5554 rtr.showStatistics(); 5555 } 5556 } 5557 5558 unittest 5559 { version(showAssertTags) dbg(); 5560 const maxCount = 1000; 5561 benchmarkReadDictWords!(void)(maxCount); 5562 benchmarkReadDictWords!(size_t)(maxCount); 5563 } 5564 5565 static private void testSlice(T)(ref T x) @trusted 5566 { 5567 auto xr = x[]; 5568 } 5569 5570 bool testEqual(T, U)(ref T x, ref U y) @trusted 5571 { 5572 import std.algorithm.comparison : equal; 5573 return equal(x[], y[]); 5574 } 5575 5576 /** Check correctness when span is `span` and for each `Key` in `Keys`. */ 5577 version(unittest) 5578 private auto checkNumeric(Keys...)() 5579 if (Keys.length != 0) 5580 { 5581 import std.traits : isIntegral, isFloatingPoint; 5582 foreach (immutable it; 0 .. 1) 5583 { 5584 struct TestValueType { int i = 42; float f = 43; char ch = 'a'; } 5585 alias Value = TestValueType; 5586 import std.meta : AliasSeq; 5587 foreach (Key; Keys) 5588 { 5589 auto set = radixTreeSet!(Key); 5590 auto map = radixTreeMap!(Key, Value); 5591 5592 assert(set.empty); 5593 assert(map.empty); 5594 5595 testSlice(set); 5596 testSlice(map); 5597 5598 static assert(!set.hasValue); 5599 static assert(map.hasValue); 5600 5601 static if (is(Key == bool) || 5602 isIntegral!Key || 5603 isFloatingPoint!Key) 5604 { 5605 static if (isIntegral!Key) 5606 { 5607 immutable low = max(Key.min, -1000); 5608 immutable high = min(Key.max, 1000); 5609 immutable factor = 1; 5610 } 5611 else static if (isFloatingPoint!Key) 5612 { 5613 immutable low = -1_000; 5614 immutable high = 1_000; 5615 immutable factor = 100; 5616 } 5617 else static if (is(Key == bool)) 5618 { 5619 immutable low = false; 5620 immutable high = true; 5621 immutable factor = 1; 5622 } 5623 else 5624 { 5625 static assert(false, "Support Key " ~ Key.stringof); 5626 } 5627 immutable length = high - low + 1; 5628 5629 foreach (immutable uk_; low .. high + 1) 5630 { 5631 immutable uk = factor*uk_; 5632 immutable Key key = cast(Key)uk; 5633 5634 assert(!set.contains(key)); // `key` should not yet be in `set` 5635 assert(!map.contains(key)); // `key` should not yet be in 'map 5636 5637 assert(key !in set); // alternative syntax 5638 assert(key !in map); // alternative syntax 5639 5640 // insertion of new `key` is `true` (previously not stored) 5641 assert(set.insert(key)); 5642 assert(map.insert(key, Value.init)); 5643 5644 // `key` should now be in `set` and `map` 5645 assert(set.contains(key)); 5646 assert(map.contains(key)); 5647 5648 // reinsertion of same `key` is `false` (already in stored) 5649 assert(!set.insert(key)); 5650 assert(!map.insert(key, Value.init)); 5651 5652 // `key` should now be `stored` 5653 assert(set.contains(key)); 5654 assert(map.contains(key)); 5655 5656 // alternative syntax 5657 assert(key in set); 5658 assert(key in map); 5659 5660 if (key != Key.max) // except last value 5661 { 5662 assert(!set.contains(cast(Key)(key + 1))); // next key is not yet in set 5663 } 5664 } 5665 assert(set.length == length); 5666 } 5667 else 5668 { 5669 static assert(false, `Handle type: "` ~ Key.stringof ~ `"`); 5670 } 5671 5672 auto setDup = set.dup(); 5673 auto mapDup = map.dup(); 5674 5675 assert(testEqual(set, setDup)); 5676 assert(testEqual(map, mapDup)); 5677 5678 set.clear(); 5679 5680 static assert(map.hasValue); 5681 map.clear(); 5682 } 5683 } 5684 } 5685 5686 /** Benchmark performance and memory usage when span is `span`. */ 5687 private void benchmarkTimeAndSpace() 5688 { 5689 version(show) import std.stdio : writeln; 5690 5691 struct TestValueType { int i = 42; float f = 43; char ch = 'a'; } 5692 alias Value = TestValueType; 5693 import std.meta : AliasSeq; 5694 foreach (Key; AliasSeq!(uint)) // just benchmark uint for now 5695 { 5696 auto set = radixTreeSet!(Key); 5697 alias Set = set; 5698 assert(set.empty); 5699 5700 static assert(!set.hasValue); 5701 5702 import std.datetime.stopwatch : StopWatch, AutoStart; 5703 5704 enum n = 1_000_000; 5705 5706 immutable useUniqueRandom = false; 5707 5708 import std.range : generate, take; 5709 import std.random : uniform; 5710 auto randomSamples = generate!(() => uniform!Key).take(n); 5711 5712 { 5713 auto sw = StopWatch(AutoStart.yes); 5714 5715 foreach (immutable Key k; randomSamples) 5716 { 5717 if (useUniqueRandom) 5718 { 5719 assert(set.insert(k)); 5720 } 5721 else 5722 { 5723 set.insert(k); 5724 } 5725 5726 /* second insert of same key should always return `false` to 5727 indicate that key was already stored */ 5728 static if (false) { assert(!set.insert(k)); } 5729 } 5730 5731 version(show) writeln("trie: Added ", n, " ", Key.stringof, "s of size ", n*Key.sizeof/1e6, " megabytes in ", sw.peek()); 5732 set.showStatistics(); 5733 } 5734 5735 { 5736 auto sw = StopWatch(AutoStart.yes); 5737 bool[int] aa; 5738 5739 foreach (Key k; randomSamples) { aa[k] = true; } 5740 5741 version(show) writeln("D-AA: Added ", n, " ", Key.stringof, "s of size ", n*Key.sizeof/1e6, " megabytes in ", sw.peek()); 5742 } 5743 5744 auto map = radixTreeMap!(Key, Value); 5745 assert(map.empty); 5746 static assert(map.hasValue); 5747 5748 map.insert(Key.init, Value.init); 5749 } 5750 } 5751 5752 /// 5753 @safe pure nothrow @nogc unittest 5754 { version(showAssertTags) dbg(); 5755 struct S { int x; } 5756 5757 alias Key = S; 5758 auto set = radixTreeSet!(Key); 5759 5760 assert(!set.contains(S(43))); 5761 5762 assert(set.insert(S(43))); 5763 assert(set.contains(S(43))); 5764 5765 assert(!set.insert(S(43))); 5766 assert(set.contains(S(43))); 5767 } 5768 5769 /** Static Iota. 5770 * 5771 * TODO Move to Phobos std.range. 5772 */ 5773 template iota(size_t from, size_t to) 5774 if (from <= to) 5775 { 5776 alias iota = iotaImpl!(to - 1, from); 5777 } 5778 private template iotaImpl(size_t to, size_t now) 5779 { 5780 import std.meta : AliasSeq; 5781 static if (now >= to) 5782 { 5783 alias iotaImpl = AliasSeq!(now); 5784 } 5785 else 5786 { 5787 alias iotaImpl = AliasSeq!(now, iotaImpl!(to, now + 1)); 5788 } 5789 } 5790 5791 unittest 5792 { version(showAssertTags) dbg(); 5793 version(benchmark) benchmarkTimeAndSpace(); 5794 } 5795 5796 import nxt.qcmeman; 5797 5798 version(unittest) 5799 { 5800 import nxt.array_help : s; 5801 version(showAssertTags) import nxt.dbgio : dbg; 5802 }