1 /** 2 * Associative Array utility functions 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.utils.aa; 15 16 import std.algorithm; 17 import std.range; 18 import std.traits; 19 import std.typecons; 20 21 // *************************************************************************** 22 23 /// Polyfill for object.require 24 static if (!__traits(hasMember, object, "require")) 25 ref V require(K, V)(ref V[K] aa, K key, lazy V value = V.init) 26 { 27 auto p = key in aa; 28 if (p) 29 return *p; 30 return aa[key] = value; 31 } 32 33 unittest 34 { 35 int[int] aa; 36 aa.require(1, 2); 37 assert(aa[1] == 2); 38 aa.require(2, 3) = 4; 39 assert(aa[2] == 4); 40 aa.require(1, 5); 41 assert(aa[1] == 2); 42 aa.require(1, 6) = 7; 43 assert(aa[1] == 7); 44 } 45 46 static if (!__traits(hasMember, object, "update")) 47 { 48 /// Polyfill for object.update 49 void updatePolyfill(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) 50 if (is(typeof(create()) : V) && is(typeof(update(aa[K.init])) : V)) 51 { 52 auto p = key in aa; 53 if (p) 54 *p = update(*p); 55 else 56 aa[key] = create(); 57 } 58 59 /// Work around https://issues.dlang.org/show_bug.cgi?id=15795 60 alias update = updatePolyfill; 61 } 62 63 // https://github.com/dlang/druntime/pull/3012 64 private enum haveObjectUpdateWithVoidUpdate = is(typeof({ 65 int[int] aa; 66 .object.update(aa, 0, { return 0; }, (ref int v) { }); 67 })); 68 69 static if (!haveObjectUpdateWithVoidUpdate) 70 { 71 /// Polyfill for object.update with void update function 72 void updateVoid(K, V, C, U)(ref V[K] aa, K key, scope C create, scope U update) 73 if (is(typeof(create()) : V) && is(typeof(update(aa[K.init])) == void)) 74 { 75 // We can polyfill this in two ways. 76 // What's more expensive, copying the value, or a second key lookup? 77 enum haveObjectUpdate = __traits(hasMember, object, "update"); 78 enum valueIsExpensiveToCopy = V.sizeof > string.sizeof 79 || hasElaborateCopyConstructor!V 80 || hasElaborateDestructor!V; 81 static if (haveObjectUpdate && !valueIsExpensiveToCopy) 82 { 83 .object.update(aa, key, create, 84 (ref V v) { update(v); return v; }); 85 } 86 else 87 { 88 auto p = key in aa; 89 if (p) 90 update(*p); 91 else 92 aa[key] = create(); 93 } 94 } 95 96 /// Work around https://issues.dlang.org/show_bug.cgi?id=15795 97 alias update = updateVoid; 98 } 99 else 100 alias updateVoid = object.update; /// Use `object.update` for void update function 101 102 // Inject overload 103 static if (__traits(hasMember, object, "update")) 104 private alias update = object.update; 105 106 // *************************************************************************** 107 108 /// Get a value from an AA, and throw an exception (not an error) if not found 109 ref auto aaGet(AA, K)(auto ref AA aa, auto ref K key) 110 if (is(typeof(key in aa))) 111 { 112 import std.conv; 113 114 auto p = key in aa; 115 if (p) 116 return *p; 117 else 118 static if (is(typeof(text(key)))) 119 throw new Exception("Absent value: " ~ text(key)); 120 else 121 throw new Exception("Absent value"); 122 } 123 124 /// If key is not in aa, add it with defaultValue. 125 /// Returns a reference to the value corresponding to key. 126 ref V getOrAdd(K, V)(ref V[K] aa, auto ref K key, auto ref V defaultValue) 127 { 128 return aa.require(key, defaultValue); 129 } 130 131 /// ditto 132 ref V getOrAdd(K, V)(ref V[K] aa, auto ref K key) 133 { 134 return getOrAdd(aa, key, V.init); 135 } 136 137 unittest 138 { 139 int[int] aa; 140 aa.getOrAdd(1, 2) = 3; 141 assert(aa[1] == 3); 142 assert(aa.getOrAdd(1, 4) == 3); 143 } 144 145 /// If key is not in aa, add it with the given value, and return true. 146 /// Otherwise, return false. 147 bool addNew(K, V)(ref V[K] aa, auto ref K key, auto ref V value) 148 { 149 bool added = void; 150 updateVoid(aa, key, 151 delegate V ( ) { added = true ; return value; }, 152 delegate void(ref V v) { added = false; }, 153 ); 154 return added; 155 } 156 157 /// ditto 158 bool addNew(K, V, bool ordered, bool multi)(ref HashCollection!(K, V, ordered, multi) aa, auto ref K key, auto ref V value) 159 if (!is(V == void)) // Not a set 160 { 161 bool added = void; 162 aa.update(key, 163 delegate V ( ) { added = true ; return value; }, 164 delegate void(ref V v) { added = false; }, 165 ); 166 return added; 167 } 168 169 unittest 170 { 171 int[int] aa; 172 assert( aa.addNew(1, 2)); 173 assert(!aa.addNew(1, 3)); 174 assert(aa[1] == 2); 175 } 176 177 unittest 178 { 179 OrderedMap!(int, int) aa; 180 assert( aa.addNew(1, 2)); 181 assert(!aa.addNew(1, 3)); 182 assert(aa[1] == 2); 183 } 184 185 // *************************************************************************** 186 187 /// Key/value pair 188 struct KeyValuePair(K, V) { K key; /***/ V value; /***/ } 189 190 /// Get key/value pairs from AA 191 deprecated KeyValuePair!(K, V)[] pairs(K, V)(V[K] aa) 192 { 193 KeyValuePair!(K, V)[] result; 194 foreach (key, value; aa) 195 result ~= KeyValuePair!(K, V)(key, value); 196 return result; 197 } 198 199 /// Get key/value pairs from AA, sorted by keys 200 KeyValuePair!(K, V)[] sortedPairs(K, V)(V[K] aa) 201 { 202 KeyValuePair!(K, V)[] result; 203 foreach (key; aa.keys.sort) 204 result ~= KeyValuePair!(K, V)(key, aa[key]); 205 return result; 206 } 207 208 /// Get values from AA, sorted by keys 209 V[] sortedValues(K, V)(in V[K] aa) 210 { 211 V[] result; 212 foreach (key; aa.keys.sort()) 213 result ~= aa[key]; 214 return result; 215 } 216 217 /// Merge source into target. Return target. 218 V[K] merge(K, V)(auto ref V[K] target, V[K] source) 219 { 220 foreach (k, v; source) 221 target[k] = v; 222 return target; 223 } 224 225 unittest 226 { 227 int[int] target; 228 int[int] source = [2:4]; 229 merge(target, source); 230 assert(source == target); 231 232 target = [1:1, 2:2, 3:3]; 233 merge(target, source); 234 assert(target == [1:1, 2:4, 3:3]); 235 236 assert(merge([1:1], [2:2]) == [1:1, 2:2]); 237 } 238 239 unittest 240 { 241 ubyte[][string] a, b; 242 merge(a, b); 243 } 244 245 /// Slurp a range of two elements (or two-element struct/class) into an AA. 246 auto toAA(R)(R r) 247 if (is(typeof(r.front[1]))) 248 { 249 alias K = typeof(r.front[0]); 250 alias V = typeof(r.front[1]); 251 V[K] result; 252 foreach (pair; r) 253 { 254 assert(pair.length == 2); 255 result[pair[0]] = pair[1]; 256 } 257 return result; 258 } 259 260 /// ditto 261 auto toAA(R)(R r) 262 if (is(typeof(r.front.tupleof)) && r.front.tupleof.length == 2 && !is(typeof(r.front[1]))) 263 { 264 return r.map!(el => tuple(el.tupleof)).toAA(); 265 } 266 267 deprecated unittest 268 { 269 assert([[2, 4]].toAA() == [2:4]); 270 assert([2:4].pairs.toAA() == [2:4]); 271 } 272 273 /// Ensure that arr is non-null if empty. 274 V[K] nonNull(K, V)(V[K] aa) 275 { 276 if (aa !is null) 277 return aa; 278 aa[K.init] = V.init; 279 aa.remove(K.init); 280 assert(aa !is null); 281 return aa; 282 } 283 284 unittest 285 { 286 int[int] aa; 287 assert(aa is null); 288 aa = aa.nonNull; 289 assert(aa !is null); 290 assert(aa.length == 0); 291 } 292 293 // *************************************************************************** 294 295 // Helpers for HashCollection 296 private 297 { 298 alias Void = void[0]; // Zero-sized type 299 static assert(Void.sizeof == 0); 300 301 // Abstraction layer for single/multi-value type holding one or many T. 302 // Optimizer representation for Void. 303 struct SingleOrMultiValue(bool multi, T) 304 { 305 alias ValueType = Select!(multi, 306 // multi==true 307 Select!(is(T == Void), 308 size_t, // "store" the items by keeping track of their count only. 309 T[], 310 ), 311 312 // multi==false 313 Select!(is(T == Void), 314 Void, 315 T[1], 316 ), 317 ); 318 319 // Using free functions instead of struct methods, 320 // as structs always have non-zero size. 321 static: 322 323 size_t length(ref const ValueType v) nothrow 324 { 325 static if (is(T == Void)) 326 static if (multi) 327 return v; // count 328 else 329 return 1; 330 else 331 return v.length; // static or dynamic array 332 } 333 } 334 } 335 336 /// Base type for ordered/unordered single-value/multi-value map/set 337 /*private*/ struct HashCollection(K, V, bool ordered, bool multi) 338 { 339 private: 340 enum bool haveValues = !is(V == void); // Not a set 341 342 // The type for values used when a value variable is needed 343 alias ValueVarType = Select!(haveValues, V, Void); 344 345 // The type of a single element of the values of `this.lookup`. 346 // When ordered==true, we use size_t (index into `this.items`). 347 alias LookupItem = Select!(ordered, size_t, ValueVarType); 348 349 // The type of the values of `this.lookup`. 350 alias SM = SingleOrMultiValue!(multi, LookupItem); 351 alias LookupValue = SM.ValueType; 352 353 static if (haveValues) 354 { 355 alias ReturnType(Fallback) = V; 356 alias OpIndexKeyType = K; 357 alias OpIndexValueType = V; 358 } 359 else 360 { 361 static if (ordered) 362 { 363 alias OpIndexKeyType = size_t; 364 alias OpIndexValueType = K; 365 alias ReturnType(Fallback) = K; 366 } 367 else 368 { 369 alias OpIndexKeyType = void; 370 alias OpIndexValueType = void; 371 alias ReturnType(Fallback) = Fallback; 372 } 373 } 374 enum haveReturnType = !is(ReturnType!void == void); 375 enum haveIndexing = haveValues || ordered; 376 377 alias IK = OpIndexKeyType; 378 alias IV = OpIndexValueType; 379 380 // The contract we try to follow is that adding/removing items in 381 // one copy of the object will not affect other copies. 382 // Therefore, when we have array fields, make sure they are dup'd 383 // on copy, so that we don't trample older copies' data. 384 enum bool needDupOnCopy = ordered; 385 386 static if (haveReturnType) 387 { 388 static if (ordered) 389 /* */ ref inout(ReturnType!void) lookupToReturnValue(in LookupItem lookupItem) inout { return items[lookupItem].returnValue; } 390 else 391 static ref inout(ReturnType!void) lookupToReturnValue(ref inout(LookupItem) lookupItem) { return lookupItem ; } 392 } 393 394 // *** Data *** 395 396 // This is used for all key hash lookups. 397 LookupValue[K] lookup; 398 399 static if (ordered) 400 { 401 struct Item 402 { 403 K key; 404 ValueVarType value; 405 406 static if (haveValues) 407 private alias returnValue = value; 408 else 409 private alias returnValue = key; 410 } 411 Item[] items; 412 413 enum bool canDup = is(typeof(lookup.dup)) && is(typeof(items.dup)); 414 } 415 else 416 { 417 enum bool canDup = is(typeof(lookup.dup)); 418 } 419 420 public: 421 422 // *** Lifetime *** 423 424 /// Postblit 425 static if (needDupOnCopy) 426 { 427 static if (canDup) 428 this(this) 429 { 430 lookup = lookup.dup; 431 items = items.dup; 432 } 433 else 434 @disable this(this); 435 } 436 437 /// Create shallow copy 438 static if (canDup) 439 typeof(this) dup() 440 { 441 static if (needDupOnCopy) 442 return this; 443 else 444 { 445 typeof(this) copy; 446 copy.lookup = lookup.dup; 447 static if (ordered) 448 copy.items = items.dup; 449 return copy; 450 } 451 } 452 453 // *** Conversions (from) *** 454 455 /// Construct from something else 456 this(Input)(Input input) 457 if (is(typeof(opAssign(input)))) 458 { 459 opAssign(input); 460 } 461 462 /// Null assignment 463 ref typeof(this) opAssign(typeof(null) _) 464 { 465 clear(); 466 return this; 467 } 468 469 /// Convert from an associative type 470 ref typeof(this) opAssign(AA)(AA aa) 471 if (haveValues 472 && !is(AA : typeof(this)) 473 && is(typeof({ foreach (ref k, ref v; aa) add(k, v); }))) 474 { 475 clear(); 476 foreach (ref k, ref v; aa) 477 add(k, v); 478 return this; 479 } 480 481 /// Convert from an associative type of multiple items 482 ref typeof(this) opAssign(AA)(AA aa) 483 if (haveValues 484 && multi 485 && !is(AA : typeof(this)) 486 && is(typeof({ foreach (ref k, ref vs; aa) foreach (ref v; vs) add(k, v); }))) 487 { 488 clear(); 489 foreach (ref k, ref vs; aa) 490 foreach (ref v; vs) 491 add(k, v); 492 return this; 493 } 494 495 /// Convert from a range of tuples 496 ref typeof(this) opAssign(R)(R input) 497 if (haveValues 498 && is(typeof({ foreach (ref pair; input) add(pair[0], pair[1]); })) 499 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); })) 500 && is(typeof(input.front.length)) 501 && input.front.length == 2) 502 { 503 clear(); 504 foreach (ref pair; input) 505 add(pair[0], pair[1]); 506 return this; 507 } 508 509 /// Convert from a range of key/value pairs 510 ref typeof(this) opAssign(R)(R input) 511 if (haveValues 512 && is(typeof({ foreach (ref pair; input) add(pair.key, pair.value); })) 513 && !is(typeof({ foreach (ref k, ref v; input) add(k, v); }))) 514 { 515 clear(); 516 foreach (ref pair; input) 517 add(pair.key, pair.value); 518 return this; 519 } 520 521 /// Convert from a range of values 522 ref typeof(this) opAssign(R)(R input) 523 if (!haveValues 524 && !is(R : typeof(this)) 525 && is(typeof({ foreach (ref v; input) add(v); }))) 526 { 527 clear(); 528 foreach (ref v; input) 529 add(v); 530 return this; 531 } 532 533 // *** Conversions (to) *** 534 535 /// Convert to bool (true if non-null) 536 bool opCast(T)() const 537 if (is(T == bool)) 538 { 539 return lookup !is null; 540 } 541 542 /// Convert to D associative array 543 static if (!ordered) 544 { 545 const(LookupValue[K]) toAA() const 546 { 547 return lookup; 548 } 549 550 static if (is(typeof(lookup.dup))) 551 LookupValue[K] toAA() 552 { 553 return lookup.dup; 554 } 555 556 deprecated alias items = toAA; 557 } 558 559 // *** Query (basic) *** 560 561 /// True when there are no items. 562 bool empty() pure const nothrow @nogc @trusted 563 { 564 static if (ordered) 565 return items.length == 0; // optimization 566 else 567 return lookup.byKey.empty; // generic version 568 } 569 570 /// Total number of items, including with duplicate keys. 571 size_t length() pure const nothrow @nogc @trusted 572 { 573 static if (ordered) 574 return items.length; // optimization 575 else 576 static if (!multi) 577 return lookup.length; // optimization 578 else // generic version 579 { 580 size_t result; 581 foreach (ref v; lookup.byValue) 582 result += SM.length(v); 583 return result; 584 } 585 } 586 587 // *** Query (by key) *** 588 589 /// Check if item with this key has been added. 590 /// When applicable, return a pointer to the last value added with this key. 591 Select!(haveReturnType, inout(ReturnType!void)*, bool) opBinaryRight(string op : "in", _K)(auto ref _K key) inout 592 if (is(typeof(key in lookup))) 593 { 594 enum missValue = select!haveReturnType(null, false); 595 596 auto p = key in lookup; 597 if (!p) 598 return missValue; 599 600 static if (haveReturnType) 601 return &lookupToReturnValue((*p)[$-1]); 602 else 603 return true; 604 } 605 606 /// Index operator. 607 /// The key must exist. Indexing with a key which does not exist 608 /// is an error. 609 static if (haveIndexing) 610 ref inout(IV) opIndex()(auto ref IK k) inout 611 { 612 static if (haveValues) 613 return lookupToReturnValue(lookup[k][$-1]); 614 else 615 return items[k].returnValue; 616 } 617 618 /// Retrieve last value associated with key, or `defaultValue` if none. 619 static if (haveIndexing) 620 auto ref inout(IV) get()(auto ref IK k, auto ref inout(IV) defaultValue) inout 621 { 622 static if (haveValues) 623 { 624 auto p = k in lookup; 625 return p ? lookupToReturnValue((*p)[$-1]) : defaultValue; 626 } 627 else 628 return k < items.length ? items[k].returnValue : defaultValue; 629 } 630 631 // *** Query (ranges) *** 632 633 /// Return a range which iterates over key/value pairs. 634 static if (haveValues) 635 auto byKeyValue(this This)() 636 { 637 static if (ordered) 638 return items; 639 else 640 { 641 return lookup 642 .byKeyValue 643 .map!(pair => 644 pair 645 .value 646 .map!(value => KeyValuePair!(K, V)(pair.key, value)) 647 ) 648 .joiner; 649 } 650 } 651 652 /// ditto 653 static if (haveValues) 654 auto byPair(this This)() 655 { 656 return byKeyValue 657 .map!(pair => tuple!("key", "value")(pair.key, pair.value)); 658 } 659 660 /// Return a range which iterates over all keys. 661 /// Duplicate keys will occur several times in the range. 662 auto byKey(this This)() 663 { 664 static if (ordered) 665 { 666 static ref getKey(MItem)(ref MItem item) { return item.key; } 667 return items.map!getKey; 668 } 669 else 670 { 671 return lookup 672 .byKeyValue 673 .map!(pair => 674 pair.key.repeat(SM.length(pair.value)) 675 ) 676 .joiner; 677 } 678 } 679 680 /// Return a range which iterates over all values. 681 static if (haveValues) 682 auto byValue(this This)() 683 { 684 static if (ordered) 685 { 686 static ref getValue(MItem)(ref MItem item) { return item.value; } 687 return items.map!getValue; 688 } 689 else 690 { 691 return lookup 692 .byKeyValue 693 .map!(pair => 694 pair 695 .value 696 ) 697 .joiner; 698 } 699 } 700 701 /// Returns all keys as an array. 702 @property auto keys(this This)() { return byKey.array; } 703 704 /// Returns all values as an array. 705 @property auto values(this This)() { return byValue.array; } 706 707 // *** Query (search by key) *** 708 709 static if (ordered) 710 { 711 /// Returns index of key `k`. 712 size_t indexOf()(auto ref const K k) 713 { 714 auto p = k in lookup; 715 return p ? (*p)[0] : -1; 716 } 717 718 /// Returns all indices of key `k`. 719 size_t[] indicesOf()(auto ref const K k) 720 { 721 auto p = k in lookup; 722 return p ? (*p)[] : null; 723 } 724 } 725 726 /// Return the number of items with the given key. 727 /// When multi==false, always returns 0 or 1. 728 size_t count()(auto ref K k) 729 { 730 static if (ordered) 731 return indicesOf(k).length; 732 else 733 { 734 auto p = k in lookup; 735 return p ? SM.length(*p) : 0; 736 } 737 } 738 739 /// Return a range with all values with the given key. 740 /// If the key is not present, returns an empty range. 741 static if (haveValues) 742 auto byValueOf(this This)(auto ref K k) 743 { 744 static if (ordered) 745 return indicesOf(k).map!(index => items[index].value); 746 else 747 return valuesOf(k); 748 } 749 750 /// Return an array with all values with the given key. 751 /// If the key is not present, returns an empty array. 752 static if (haveValues) 753 V[] valuesOf()(auto ref K k) 754 { 755 static if (ordered) 756 return byValueOf(k).array; 757 else 758 { 759 static if (multi) 760 return lookup.get(k, null); 761 else 762 { 763 auto p = k in lookup; 764 return p ? (*p)[] : null; 765 } 766 } 767 } 768 769 static if (haveValues) 770 deprecated alias getAll = valuesOf; 771 772 // *** Iteration *** 773 774 // Note: When iterating over keys in an AA, you must choose 775 // mutable OR ref, but not both. This is an important reason for 776 // the complexity below. 777 778 private enum isParameterRef(size_t index, fun...) = (){ 779 foreach (keyStorageClass; __traits(getParameterStorageClasses, fun[0], index)) 780 if (keyStorageClass == "ref") 781 return true; 782 return false; 783 }(); 784 785 private int opApplyImpl(this This, Dg)(scope Dg dg) 786 { 787 enum single = arity!dg == 1; 788 789 int result = 0; 790 791 static if (ordered) 792 { 793 foreach (ref item; items) 794 { 795 static if (single) 796 result = dg(item.returnValue); 797 else 798 result = dg(item.key, item.value); 799 if (result) 800 break; 801 } 802 } 803 else 804 { 805 static if (single && haveValues) 806 { 807 // Dg accepts value only, so use whatever we want for the key iteration. 808 alias LK = const(K); 809 enum useRef = true; 810 } 811 else 812 { 813 // Dg accepts a key (and maybe a value), so use the Dg signature for iteration. 814 alias LK = Parameters!Dg[0]; 815 enum useRef = isParameterRef!(0, Dg); 816 } 817 // LookupValue or const(LookupValue), depending on the constness of This 818 alias LV = typeof(lookup.values[0]); 819 820 bool handle()(ref LK key, ref LV values) 821 { 822 static if (haveValues) 823 { 824 foreach (ref value; values) 825 { 826 static if (single) 827 result = dg(value); 828 else 829 result = dg(key, value); 830 if (result) 831 return false; 832 } 833 } 834 else 835 { 836 foreach (iteration; 0 .. SM.length(values)) 837 { 838 static assert(single); 839 result = dg(key); 840 if (result) 841 return false; 842 } 843 } 844 return true; 845 } 846 847 static if (useRef) 848 { 849 foreach (ref LK key, ref LV values; lookup) 850 if (!handle(key, values)) 851 break; 852 } 853 else 854 { 855 foreach (LK key, ref LV values; lookup) 856 if (!handle(key, values)) 857 break; 858 } 859 } 860 return result; 861 } 862 863 private alias KeyIterationType(bool isConst, bool byRef) = typeof(*(){ 864 865 static if (isConst) 866 const bool[K] aa; 867 else 868 bool[K] aa; 869 870 static if (byRef) 871 foreach (ref k, v; aa) 872 return &k; 873 else 874 foreach (k, v; aa) 875 return &k; 876 877 assert(false); 878 }()); 879 880 private enum needRefOverload(bool isConst) = 881 // Unfortunately this doesn't work: https://issues.dlang.org/show_bug.cgi?id=21683 882 // !is(KeyIterationType!(isConst, false) == KeyIterationType!(isConst, true)); 883 !isCopyable!K; 884 885 private template needIter(bool isConst, bool byRef) 886 { 887 static if (!isCopyable!K) 888 enum needIter = byRef; 889 else 890 static if (!byRef) 891 enum needIter = true; 892 else 893 enum needIter = needRefOverload!isConst; 894 } 895 896 static if (haveValues) 897 { 898 /// Iterate over values (maps). 899 int opApply(scope int delegate( ref V) dg) { return opApplyImpl(dg); } 900 int opApply(scope int delegate(const ref V) dg) const { return opApplyImpl(dg); } /// ditto 901 } 902 else 903 { 904 /// Iterate over keys (sets). 905 static if (needIter!(false, false)) int opApply(scope int delegate( KeyIterationType!(false, false)) dg) { return opApplyImpl(dg); } 906 static if (needIter!(true , false)) int opApply(scope int delegate( KeyIterationType!(true , false)) dg) const { return opApplyImpl(dg); } /// ditto 907 static if (needIter!(false, true )) int opApply(scope int delegate(ref KeyIterationType!(false, true )) dg) { return opApplyImpl(dg); } /// ditto 908 static if (needIter!(true , true )) int opApply(scope int delegate(ref KeyIterationType!(true , true )) dg) const { return opApplyImpl(dg); } /// ditto 909 } 910 911 static if (haveValues) 912 { 913 /// Iterate over keys and values. 914 static if (needIter!(false, false)) int opApply(scope int delegate( KeyIterationType!(false, false), ref V) dg) { return opApplyImpl(dg); } 915 static if (needIter!(true , false)) int opApply(scope int delegate( KeyIterationType!(true , false), const ref V) dg) const { return opApplyImpl(dg); } /// ditto 916 static if (needIter!(false, true )) int opApply(scope int delegate(ref KeyIterationType!(false, true ), ref V) dg) { return opApplyImpl(dg); } /// ditto 917 static if (needIter!(true , true )) int opApply(scope int delegate(ref KeyIterationType!(true , true ), const ref V) dg) const { return opApplyImpl(dg); } /// ditto 918 } 919 920 private struct ByRef(bool isConst) 921 { 922 static if (isConst) 923 const(HashCollection)* c; 924 else 925 HashCollection* c; 926 927 static if (haveValues) 928 { 929 static if (isConst) 930 int opApply(scope int delegate(ref KeyIterationType!(true , true ), const ref V) dg) const { return c.opApplyImpl(dg); } 931 else 932 int opApply(scope int delegate(ref KeyIterationType!(false, true ), ref V) dg) { return c.opApplyImpl(dg); } 933 } 934 else 935 { 936 static if (isConst) 937 int opApply(scope int delegate(ref KeyIterationType!(true , true )) dg) const { return c.opApplyImpl(dg); } 938 else 939 int opApply(scope int delegate(ref KeyIterationType!(false, true )) dg) { return c.opApplyImpl(dg); } 940 } 941 } 942 943 /// Returns an object that allows iterating over this collection with ref keys. 944 /// Workaround for https://issues.dlang.org/show_bug.cgi?id=21683 945 auto byRef() return { return ByRef!false(&this); } 946 auto byRef() const return { return ByRef!true (&this); } /// ditto 947 948 // *** Mutation (addition) *** 949 950 private enum AddMode 951 { 952 add, /// Always add value 953 replace, /// Replace all previous values 954 require, /// Only add value if it did not exist before 955 } 956 957 private ref ReturnType!void addImpl(AddMode mode, AK, GV)(ref AK key, scope GV getValue) 958 if (is(AK : K)) 959 { 960 static if (ordered) 961 { 962 size_t addedIndex; 963 964 static if (multi && mode == AddMode.add) 965 { 966 addedIndex = items.length; 967 lookup[key] ~= addedIndex; 968 items ~= Item(key, getValue()); 969 } 970 else 971 { 972 lookup.updateVoid(key, 973 delegate LookupValue() 974 { 975 addedIndex = items.length; 976 items ~= Item(key, getValue()); 977 return [addedIndex]; 978 }, 979 delegate void(ref LookupValue existingIndex) 980 { 981 addedIndex = existingIndex[0]; 982 static if (mode != AddMode.require) 983 { 984 static if (multi) 985 { 986 static assert(mode == AddMode.replace); 987 existingIndex = existingIndex[0 .. 1]; 988 } 989 items[addedIndex].value = getValue(); 990 } 991 }); 992 } 993 994 return items[addedIndex].returnValue; 995 } 996 else // ordered 997 { 998 static if (haveValues) 999 { 1000 static if (mode == AddMode.require) 1001 return (lookup.require(key, [getValue()]))[0]; 1002 else 1003 static if (multi && mode == AddMode.add) 1004 return (lookup[key] ~= getValue())[$-1]; 1005 else 1006 return (lookup[key] = [getValue()])[0]; 1007 } 1008 else 1009 { 1010 static if (multi) 1011 { 1012 static if (mode == AddMode.require) 1013 lookup.require(key, 1); 1014 else 1015 static if (mode == AddMode.add) 1016 lookup[key]++; 1017 else 1018 lookup[key] = 1; 1019 } 1020 else 1021 lookup[key] = LookupValue.init; 1022 // This branch returns void, as there is no reasonable 1023 // ref to an AA key that we can return here. 1024 } 1025 } 1026 } 1027 1028 /*private*/ template _addSetFunc(AddMode mode) 1029 { 1030 static if (haveValues) 1031 { 1032 ref ReturnType!void _addSetFunc(AK, AV)(auto ref AK key, auto ref AV value) 1033 if (is(AK : K) && is(AV : V)) 1034 { 1035 return addImpl!mode(key, () => value); 1036 } 1037 } 1038 else 1039 { 1040 ref ReturnType!void _addSetFunc(AK)(auto ref AK key) 1041 if (is(AK : K)) 1042 { 1043 ValueVarType value; // void[0] 1044 return addImpl!mode(key, () => value); 1045 } 1046 } 1047 } 1048 1049 /// Add an item. 1050 alias add = _addSetFunc!(AddMode.add); 1051 1052 /// Ensure a key exists (with the given value). 1053 /// When `multi==true`, replaces all previous entries with this key. 1054 /// Otherwise, behaves identically to `add`. 1055 alias set = _addSetFunc!(AddMode.replace); 1056 1057 /// Add `value` only if `key` is not present. 1058 static if (haveValues) 1059 ref V require()(auto ref K key, lazy V value = V.init) 1060 { 1061 return addImpl!(AddMode.require)(key, () => value); 1062 } 1063 1064 deprecated alias getOrAdd = require; 1065 1066 private alias UpdateFuncRT(U) = typeof({ U u = void; V v = void; return u(v); }()); 1067 1068 /// If `key` is present, call `update` for every value; 1069 /// otherwise, add new value with `create`. 1070 static if (haveValues) 1071 void update(C, U)(auto ref K key, scope C create, scope U update) 1072 if (is(typeof(create()) : V) && (is(UpdateFuncRT!U : V) || is(UpdateFuncRT!U == void))) 1073 { 1074 static if (ordered) 1075 { 1076 lookup.updateVoid(key, 1077 delegate LookupValue() 1078 { 1079 auto addedIndex = items.length; 1080 items ~= Item(key, create()); 1081 return [addedIndex]; 1082 }, 1083 delegate void(ref LookupValue existingIndex) 1084 { 1085 foreach (i; existingIndex) 1086 static if (is(UpdateFuncRT!U == void)) 1087 update(items[i].value); 1088 else 1089 items[i].value = update(items[i].value); 1090 }); 1091 } 1092 else // ordered 1093 { 1094 lookup.updateVoid(key, 1095 delegate LookupValue () 1096 { 1097 return [create()]; 1098 }, 1099 delegate void (ref LookupValue values) 1100 { 1101 foreach (ref value; values) 1102 static if (is(UpdateFuncRT!U == void)) 1103 update(value); 1104 else 1105 value = update(value); 1106 }); 1107 } 1108 } 1109 1110 // *** Mutation (editing) *** 1111 1112 static if (haveIndexing) 1113 { 1114 static if (haveValues) 1115 { 1116 /// Same as `set(k, v)`. 1117 ref IV opIndexAssign(AK, AV)(auto ref AV v, auto ref AK k) 1118 if (is(AK : K) && is(AV : V)) 1119 { 1120 return set(k, v); 1121 } 1122 1123 /// Perform cumulative operation with value 1124 /// (initialized with `.init` if the key does not exist). 1125 ref IV opIndexOpAssign(string op, AK, AV)(auto ref AV v, auto ref AK k) 1126 if (is(AK : K) && is(AV : V)) 1127 { 1128 auto pv = &require(k); 1129 return mixin("(*pv) " ~ op ~ "= v"); 1130 } 1131 1132 /// Perform unary operation with value 1133 /// (initialized with `.init` if the key does not exist). 1134 ref IV opIndexUnary(string op, AK)(auto ref AK k) 1135 if (is(AK : K)) 1136 { 1137 auto pv = &require(k); 1138 mixin("(*pv) " ~ op ~ ";"); 1139 return *pv; 1140 } 1141 } 1142 else 1143 { 1144 private ref K editIndex(size_t index, scope void delegate(ref K) edit) 1145 { 1146 auto item = &items[index]; 1147 K oldKey = item.key; 1148 auto pOldIndices = oldKey in lookup; 1149 assert(pOldIndices); 1150 1151 edit(item.key); 1152 1153 // Add new value 1154 1155 lookup.updateVoid(item.key, 1156 delegate LookupValue() 1157 { 1158 // New value did not exist. 1159 if ((*pOldIndices).length == 1) 1160 { 1161 // Optimization - migrate the Indexes value 1162 assert((*pOldIndices)[0] == index); 1163 return *pOldIndices; 1164 } 1165 else 1166 return [index]; 1167 }, 1168 delegate void(ref LookupValue existingIndex) 1169 { 1170 // Value(s) with the new key already existed 1171 static if (multi) 1172 existingIndex ~= index; 1173 else 1174 assert(false, "Collision after in-place edit of a non-multi ordered set element"); 1175 }); 1176 1177 // Remove old value 1178 1179 if ((*pOldIndices).length == 1) 1180 lookup.remove(oldKey); 1181 else 1182 static if (multi) 1183 *pOldIndices = (*pOldIndices).remove!(i => i == index); 1184 else 1185 assert(false); // Should be unreachable (`if` above will always be true) 1186 1187 return item.key; 1188 } 1189 1190 /// Allows writing to ordered sets by index. 1191 /// The total number of elements never changes as a result 1192 /// of such an operation - a consequence of which is that 1193 /// if multi==false, changing the value to one that's 1194 /// already in the set is an error. 1195 ref IV opIndexAssign()(auto ref IV v, auto ref IK k) 1196 { 1197 static if (haveValues) 1198 return set(k, v); 1199 else 1200 return editIndex(k, (ref IV e) { e = v; }); 1201 } 1202 1203 /// Perform cumulative operation with value at index. 1204 ref IV opIndexOpAssign(string op)(auto ref VV v, auto ref IK k) 1205 { 1206 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ "= v;"); }); 1207 } 1208 1209 /// Perform unary operation with value at index. 1210 ref IV opIndexUnary(string op)(auto ref IK k) 1211 { 1212 return editIndex(k, (ref IV e) { mixin("e " ~ op ~ ";"); }); 1213 } 1214 } 1215 } 1216 1217 // *** Mutation (removal) *** 1218 1219 /// Removes all elements with the given key. 1220 bool remove(AK)(auto ref AK key) 1221 if (is(typeof(lookup.remove(key)))) 1222 { 1223 static if (ordered) 1224 { 1225 auto p = key in lookup; 1226 if (!p) 1227 return false; 1228 1229 auto targets = *p; 1230 foreach (target; targets) 1231 { 1232 items = items.remove!(SwapStrategy.stable)(target); 1233 foreach (ref k, ref vs; lookup) 1234 foreach (ref v; vs) 1235 if (v > target) 1236 v--; 1237 } 1238 auto success = lookup.remove(key); 1239 assert(success); 1240 return true; 1241 } 1242 else 1243 return lookup.remove(key); 1244 } 1245 1246 /// Removes all elements. 1247 void clear() 1248 { 1249 lookup.clear(); 1250 static if (ordered) 1251 items = null; 1252 } 1253 } 1254 1255 /// An associative array which retains the order in which elements were added. 1256 alias OrderedMap(K, V) = HashCollection!(K, V, true, false); 1257 1258 unittest 1259 { 1260 alias M = OrderedMap!(string, int); 1261 M m; 1262 m["a"] = 1; 1263 m["b"] = 2; 1264 m["c"] = 3; 1265 assert(m.length == 3); 1266 assert("a" in m); 1267 assert("d" !in m); 1268 1269 { 1270 auto r = m.byKeyValue; 1271 assert(!r.empty); 1272 assert(r.front.key == "a"); 1273 r.popFront(); 1274 assert(!r.empty); 1275 assert(r.front.key == "b"); 1276 r.popFront(); 1277 assert(!r.empty); 1278 assert(r.front.key == "c"); 1279 r.popFront(); 1280 assert(r.empty); 1281 } 1282 1283 assert(m.byKey.equal(["a", "b", "c"])); 1284 assert(m.byValue.equal([1, 2, 3])); 1285 assert(m.byKeyValue.map!(p => p.key).equal(m.byKey)); 1286 assert(m.byKeyValue.map!(p => p.value).equal(m.byValue)); 1287 assert(m.keys == ["a", "b", "c"]); 1288 assert(m.values == [1, 2, 3]); 1289 1290 { 1291 const(M)* c = &m; 1292 assert(c.byKey.equal(["a", "b", "c"])); 1293 assert(c.byValue.equal([1, 2, 3])); 1294 assert(c.keys == ["a", "b", "c"]); 1295 assert(c.values == [1, 2, 3]); 1296 } 1297 1298 m.byValue.front = 5; 1299 assert(m.byValue.equal([5, 2, 3])); 1300 1301 m.remove("a"); 1302 assert(m.length == 2); 1303 m["x"] -= 1; 1304 assert(m["x"] == -1); 1305 ++m["y"]; 1306 assert(m["y"] == 1); 1307 auto cm = cast(const)m.dup; 1308 foreach (k, v; cm) 1309 if (k == "x") 1310 assert(v == -1); 1311 } 1312 1313 unittest 1314 { 1315 OrderedMap!(string, int) m; 1316 m["a"] = 1; 1317 m["b"] = 2; 1318 m.remove("a"); 1319 assert(m["b"] == 2); 1320 } 1321 1322 unittest 1323 { 1324 OrderedMap!(string, int) m; 1325 m["a"] = 1; 1326 auto m2 = m; 1327 m2.remove("a"); 1328 m2["b"] = 2; 1329 assert(m["a"] == 1); 1330 } 1331 1332 unittest 1333 { 1334 OrderedMap!(string, int) m; 1335 m["a"] = 1; 1336 m["b"] = 2; 1337 auto m2 = m; 1338 m.remove("a"); 1339 assert(m2["a"] == 1); 1340 } 1341 1342 unittest 1343 { 1344 class C {} 1345 const OrderedMap!(string, C) m; 1346 cast(void)m.byKeyValue; 1347 } 1348 1349 unittest 1350 { 1351 OrderedMap!(int, int) m; 1352 m.update(10, 1353 { return 20; }, 1354 (ref int k) { k++; return 30; }, 1355 ); 1356 assert(m.length == 1 && m[10] == 20); 1357 m.update(10, 1358 { return 40; }, 1359 (ref int k) { k++; return 50; }, 1360 ); 1361 assert(m.length == 1 && m[10] == 50); 1362 } 1363 1364 // https://issues.dlang.org/show_bug.cgi?id=18606 1365 unittest 1366 { 1367 struct S 1368 { 1369 struct T 1370 { 1371 int foo; 1372 int[] bar; 1373 } 1374 1375 OrderedMap!(int, T) m; 1376 } 1377 } 1378 1379 unittest 1380 { 1381 OrderedMap!(string, int) m; 1382 static assert(is(typeof(m.keys))); 1383 static assert(is(typeof(m.values))); 1384 } 1385 1386 unittest 1387 { 1388 OrderedMap!(string, int) m; 1389 foreach (k, v; m) 1390 k = k ~ k; 1391 } 1392 1393 unittest 1394 { 1395 struct S { @disable this(); } 1396 const OrderedMap!(string, S) m; 1397 } 1398 1399 /// Like assocArray 1400 auto orderedMap(R)(R input) 1401 if (is(typeof(input.front.length) : size_t) && input.front.length == 2) 1402 { 1403 alias K = typeof(input.front[0]); 1404 alias V = typeof(input.front[1]); 1405 return OrderedMap!(K, V)(input); 1406 } 1407 1408 auto orderedMap(R)(R input) 1409 if (is(typeof(input.front.key)) && is(typeof(input.front.value)) && !is(typeof(input.front.length))) 1410 { 1411 alias K = typeof(input.front.key); 1412 alias V = typeof(input.front.value); 1413 return OrderedMap!(K, V)(input); 1414 } /// ditto 1415 1416 unittest 1417 { 1418 auto map = 3.iota.map!(n => tuple(n, n + 1)).orderedMap; 1419 assert(map.length == 3 && map[1] == 2); 1420 } 1421 1422 unittest 1423 { 1424 OrderedMap!(string, int) m; 1425 m = m.byKeyValue.orderedMap; 1426 m = m.byPair.orderedMap; 1427 } 1428 1429 // *************************************************************************** 1430 1431 /// Helper/wrapper for void[0][T] 1432 alias HashSet(T) = HashCollection!(T, void, false, false); 1433 1434 unittest 1435 { 1436 HashSet!int s; 1437 assert(!s); 1438 assert(s.length == 0); 1439 assert(!(1 in s)); 1440 assert(1 !in s); 1441 s.add(1); 1442 assert(1 in s); 1443 assert(s.length == 1); 1444 foreach (k; s) 1445 assert(k == 1); 1446 foreach (ref k; s.byRef) 1447 assert(k == 1); 1448 s.remove(1); 1449 assert(s.length == 0); 1450 1451 s.add(1); 1452 auto t = s.dup; 1453 s.add(2); 1454 assert(t.length==1); 1455 t.remove(1); 1456 assert(t.length==0); 1457 } 1458 1459 unittest 1460 { 1461 struct S { int[int] aa; } 1462 HashSet!S set; 1463 S s; 1464 set.add(s); 1465 assert(s in set); 1466 } 1467 1468 /// Construct a set from the range `r`. 1469 auto toSet(R)(R r) 1470 { 1471 alias E = ElementType!R; 1472 return HashSet!E(r); 1473 } 1474 1475 unittest 1476 { 1477 auto set = [1, 2, 3].toSet(); 1478 assert(2 in set); 1479 assert(4 !in set); 1480 } 1481 1482 unittest 1483 { 1484 HashSet!int m; 1485 const int i; 1486 m.remove(i); 1487 } 1488 1489 unittest 1490 { 1491 HashSet!Object m; 1492 Object o; 1493 m.remove(o); 1494 } 1495 1496 unittest 1497 { 1498 struct S 1499 { 1500 @disable this(this); 1501 } 1502 1503 HashSet!S set; 1504 } 1505 1506 // *************************************************************************** 1507 1508 /// An ordered set of `T`, which retains 1509 /// the order in which elements are added. 1510 alias OrderedSet(T) = HashCollection!(T, void, true, false); 1511 1512 unittest 1513 { 1514 OrderedSet!int set; 1515 1516 assert(1 !in set); 1517 set.add(1); 1518 assert(1 in set); 1519 set.remove(1); 1520 assert(1 !in set); 1521 1522 set.add(1); 1523 set.clear(); 1524 assert(1 !in set); 1525 1526 set = set.init; 1527 assert(!set); 1528 set.add(1); 1529 assert(!!set); 1530 1531 assert(set[0] == 1); 1532 set[0] = 2; 1533 assert(set[0] == 2); 1534 assert(1 !in set); 1535 assert(2 in set); 1536 1537 assert(set.length == 1); 1538 set.remove(2); 1539 assert(set.length == 0); 1540 1541 set.add(1); 1542 auto set2 = set; 1543 set.remove(1); 1544 set.add(2); 1545 assert(1 !in set && 2 in set); 1546 assert(1 in set2 && 2 !in set2); 1547 1548 foreach (v; set) 1549 assert(v == 2); 1550 } 1551 1552 /// Construct an ordered set from the range `r`. 1553 auto orderedSet(R)(R r) 1554 { 1555 alias E = ElementType!R; 1556 return OrderedSet!E(r); 1557 } 1558 1559 // *************************************************************************** 1560 1561 /// An object which acts mostly as an associative array, 1562 /// with the added property of being able to hold keys with 1563 /// multiple values. These are only exposed explicitly and 1564 /// through iteration 1565 alias MultiAA(K, V) = HashCollection!(K, V, false, true); 1566 1567 unittest 1568 { 1569 alias MASS = MultiAA!(string, int); 1570 MASS aa; 1571 aa.add("foo", 42); 1572 assert(aa["foo"] == 42); 1573 assert(aa.valuesOf("foo") == [42]); 1574 assert(aa.byPair.front.key == "foo"); 1575 1576 auto aa2 = MASS([tuple("foo", 42)]); 1577 aa2 = ["a":1,"b":2]; 1578 1579 const int i; 1580 aa["a"] = i; 1581 } 1582 1583 unittest 1584 { 1585 MultiAA!(int, int) m; 1586 int[][int] a; 1587 m = a; 1588 }