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