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